hardening: prepared statements, PHP 7.4 idioms, and security fixes#8
hardening: prepared statements, PHP 7.4 idioms, and security fixes#8somethingwithproof wants to merge 3 commits intoCacti:developfrom
Conversation
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
… equivalents Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
There was a problem hiding this comment.
Pull request overview
This PR hardens the WMI plugin by reducing SQL injection risk via prepared statements and tightening command/credential handling in the Linux WMI execution path.
Changes:
- Converted several SQL calls to prepared statements (
db_fetch_*_prepared,db_execute_prepared). - Hardened shell command construction by escaping the WMI hostname before execution.
- Changed stored credential encoding from PHP
serialize()to base64(JSON), while retaining legacy decode support.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
script/wmi-script.php |
Fetch WMI query metadata using a prepared statement. |
poller_wmi.php |
Purge queries use prepared deletes keyed by host_id. |
linux_wmi.php |
Escape hostname for shell execution; update password encode/decode format with legacy support. |
functions.php |
Prepared query for data_input_fields lookup in XML export generation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| function decode($info) { | ||
| $info = base64_decode($info); | ||
| $info = unserialize($info); | ||
| $info = $info['password']; | ||
|
|
||
| return $info; | ||
| /* | ||
| * Legacy records were stored with serialize(). Detect and migrate on | ||
| * read so existing credentials survive the format change. New writes | ||
| * always use JSON (see encode()). | ||
| */ | ||
| if (substr($info, 0, strlen('a:')) === 'a:') { | ||
| $decoded = @unserialize($info, ['allowed_classes' => false]); | ||
| return is_array($decoded) ? ($decoded['password'] ?? '') : ''; | ||
| } | ||
|
|
||
| $decoded = json_decode($info, true); | ||
| return is_array($decoded) ? ($decoded['password'] ?? '') : ''; |
There was a problem hiding this comment.
decode() assumes base64_decode($info) returns a valid string. If $info is NULL/empty/invalid, base64_decode() can emit warnings and return non-string values, and then substr()/json_decode() will also warn and may end up treating the password as empty. Consider validating the input type and using strict base64 decoding (second arg true) with an explicit failure path before attempting legacy unserialize() / json_decode().
There was a problem hiding this comment.
Valid. Will add a null/false check on base64_decode return.
| * Legacy records were stored with serialize(). Detect and migrate on | ||
| * read so existing credentials survive the format change. New writes | ||
| * always use JSON (see encode()). |
There was a problem hiding this comment.
The block comment says legacy records are “migrated on read”, but the code only detects/decodes the legacy serialization format and does not write back the new JSON encoding. Either adjust the comment to avoid implying a database migration, or implement a real write-back migration when legacy data is successfully decoded.
| * Legacy records were stored with serialize(). Detect and migrate on | |
| * read so existing credentials survive the format change. New writes | |
| * always use JSON (see encode()). | |
| * Legacy records were stored with serialize(). Detect and decode them | |
| * during reads so existing credentials continue to work after the format | |
| * change. New writes always use JSON (see encode()). |
There was a problem hiding this comment.
Correct. The migration is read-only by design. A separate batch migration script would be needed to convert all stored records.
|
|
||
| return $a; | ||
| $a = ['password' => $info]; | ||
| return base64_encode(json_encode($a)); |
There was a problem hiding this comment.
encode() does not handle json_encode() failure. When json_encode() returns false (e.g., invalid UTF-8 in the password), base64_encode() will end up encoding an empty string, which can silently wipe stored credentials. Add error handling (e.g., check json_last_error() / use JSON_THROW_ON_ERROR and fail or fall back) so credentials are never overwritten with an empty/invalid encoding.
| return base64_encode(json_encode($a)); | |
| $encoded = json_encode($a); | |
| if ($encoded === false) { | |
| /* | |
| * Do not base64-encode a failed json_encode() result, as that can | |
| * silently store an empty/invalid credential blob. Fall back to the | |
| * legacy serialize() format, which decode() already supports. | |
| */ | |
| $this->error = 'ERROR: Failed to JSON encode WMI credentials: ' . json_last_error_msg(); | |
| $encoded = serialize($a); | |
| } | |
| return base64_encode($encoded); |
There was a problem hiding this comment.
Valid. Will add json_encode failure check.
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
03bd9c5 to
76de570
Compare
Consolidated hardening: migrate to prepared statements, null coalescing operators, PHP 7.4 str_starts_with replacement.