PHP 的鬆散比較(==)與類型轉換機制,是語言靈活性的來源,也是無數安全漏洞的溫床。本文結合真實案例——ManageWP、InfiniteWP、CMS Commander、Worpit 等主流 WordPress 遠程管理插件的簽名繞過與 RCE 漏洞,深度剖析 md5() == $signature、$_GET['token'] != $storedToken 如何被攻擊者利用,並給出修復與防禦清單。
| 運算子 | 行為 | 範例 |
|---|---|---|
== 鬆散比較 | 類型轉換後比較值 | '0e12345' == 0 // true'abc' == 0 // true'1abc' == 1 // true |
=== 嚴格比較 | 類型與值皆相同 | '0' === 0 // false |
(int)$var 強制轉整數 | 截取前導數字,非數字開頭為 0 | (int)'123abc' // 123(int)'abc' // 0 |
settype($var, 'integer') | 就地轉換變數類型 | 同上 |
<?php
// helper.class.php
// $signature 來自反序列化的用戶輸入
// $data 為 action + messageId
if (md5($data . $this->get_random_signature()) == $signature) {
// valid message → 執行 execute_php_code 等動作
}
"0e8972..." 或 "a1b2..."。$signature 類型:反序列化時傳入 i:0(整數 0)或 i:1 等。0 或非數字開頭 → (int)"0e..." == 0 → true1 開頭 → (int)"1a..." == 1 → trueexecute_php_code 動作,執行 exec('touch /tmp/owned')。<?php
// 1. 嚴格比較
if (hash_equals(md5($data . $this->get_random_signature()), (string)$signature)) {
// ...
}
// 2. 或:強制字串比較 + 檢查類型
if (is_string($signature) && md5($data . $this->get_random_signature()) === $signature) {
// ...
}
根本修正: 改用 hash_hmac('sha256', $data, $secretKey),並將 整個請求體(含參數)納入簽名,防止參數竄改。
<?php
$token = $_GET['token']; // 用戶輸入
$stored = $oWpHelper->getTransient('worpit_login_token'); // 多數情況回傳 false
if ($token != $stored) { // 鬆散比較!
die('Invalid token');
}
// 登入成功,建立管理員會話
$stored === false(Token 已用過或不存在)token=0(字串 "0")"0" != false → false(PHP 鬆散比較:(int)"0" == (int)false → 0 == 0 → true,!true → false,條件不成立,繼續執行)?worpit_api=1&m=login&token=0<?php
if (!isset($_GET['token']) || $_GET['token'] !== $oWpHelper->getTransient('worpit_login_token')) {
die('Invalid token');
}
// 並確保 Token 為隨機高熵字串,用後即銷,儲存於資料庫而非 Transient
| 問題 | 風險 | 正確做法 |
|---|---|---|
密鑰直接拼接後雜湊 md5($data . $secret) | 長度擴展攻擊、碰撞風險 | HMAC:hash_hmac('sha256', $data, $secret) |
| 簽名不覆蓋完整請求參數 | 參數竄改繞過(如改 code 參數執行任意 PHP) | 對 json_encode($fullRequest) 簽名 |
| 使用 MD5 | 碰撞已被實用化,不宜用於安全簽名 | SHA-256 / SHA-3 / BLAKE2 |
| 反序列化用戶輸入 | 物件注入、POP 鏈、RCE | 禁止 unserialize() 處理不可信資料;改用 JSON |
| 鬆散比較驗證敏感值 | 類型混淆繞過 | 全程 === / hash_equals() |
== !=,確認是否用於敏感驗證(Token、簽名、密碼、權限)。hash_equals() 比較雜湊/Token,password_verify() 驗證密碼。filter_input()、is_string()、ctype_digit() 等嚴格檢查。unserialize() 處理外部資料;採用 JSON + Schema 驗證。PHP 的類型系統不會自動保護你。每一次 ==、每一次 unserialize()、每一次自製簽名,都是潛在的災難入口。嚴格類型、標準密碼學、零信任輸入——這三條鐵律,才是守住生產環境的唯一正道。