PHP 中的比較運算符分為「鬆散比較」(==)與「嚴格比較」(===),兩者的行為差異常引發難以察覺的安全漏洞。鬆散比較會自動進行類型轉換,例如將字串轉為數字、非布林值轉為布林值,這種「盡力而為」的機制雖然方便,卻極易導致邏輯判斷錯誤,甚至被攻擊者利用繞過驗證。官方文檔明確建議:為避免不可預期的行為,應優先使用嚴格比較(===、!==),確保值與類型同時一致。
| 運算式 | 名稱 | 結果條件 |
|---|---|---|
$a == $b | 等於 | 類型轉換後 $a 等於 $b 為 TRUE |
$a === $b | 全等 | $a 等於 $b 且類型相同為 TRUE |
$a != $b | 不等 | 類型轉換後 $a 不等於 $b 為 TRUE |
$a <> $b | 不等 | 同上 |
$a !== $b | 不全等 | $a 不等於 $b 或類型不同為 TRUE |
$a < $b | 小於 | $a 嚴格小於 $b 為 TRUE |
$a > $b | 大於 | $a 嚴格大於 $b 為 TRUE |
$a <= $b | 小於等於 | $a 小於或等於 $b 為 TRUE |
$a >= $b | 大於等於 | $a 大於或等於 $b 為 TRUE |
當 Hash 值為 0e 開頭且後接純數字(如 0e123456789)時,PHP 會將其解析為科學計數法 0 × 10^n,結果為 0。若與整數 0 或字串 "0" 進行鬆散比較,將返回 true,導致驗證繞過。
var_dump("00e0345" == "0"); // bool(true)
var_dump("0e123456789" == "0"); // bool(true)
var_dump("0e1234abc" == "0"); // bool(false) 含非數字字符,不觸發科學計數法
防範: 所有密碼/Token 比對務必使用 hash_equals() 或 ===,切勿使用 ==。
json_decode() 與 unserialize() 會將 JSON 的 true/false 或序列化數據中的 b:1/b:0 還原為 PHP 布林值。若業務邏輯用 == 比較字串(如 'admin'),布林 true 會被轉換為 1,進而與非空字串判定相等。
$json = '{"user":true,"pass":true}';
$data = json_decode($json, true);
if ($data['user'] == 'admin' && $data['pass'] == 'security') {
// 實際會進入:true == 'admin' → 1 == 'admin' → true
}
防範: 輸入驗證層面強制類型檢查(is_string()),比較層面全面採用 ===。
(int) 或 intval() 轉換浮點數時會「向下取整」(0.9999 → 0),導致 $uid == "1" 判斷通過,但實際查詢卻成了 uid=0。intval() 還會從字串開頭提取數字直到遇到非數字(intval("123abc") → 123),若直接拼接 SQL 將引發注入。
// 繞過 uid != 1 檢查
$_POST['uid'] = '1.1'; // 1.1 != 1 → true
(int)$_POST['uid']; // → 1,操作管理員帳號
// SQL 注入示例
if (intval($qq) === '123456') {
$db->query("select * from user where qq = $qq");
// 攻擊傳入:123456 union select version()
}
防範: 使用 filter_var($input, FILTER_VALIDATE_INT) 嚴格驗證整數;SQL 必須採用預處理語句。
該版本引入的優化導致超長數字字串在鬆散比較時發生精度截斷,使兩個明顯不同的值判定相等:
var_dump("61529519452809720693702583126814" == "61529519452809720000000000000000"); // bool(true)
防範: 升級 PHP 版本,並堅持使用嚴格比較。
strcmp() 期望兩個參數均為字串,若傳入陣列會發出警告並返回 NULL。鬆散比較 NULL == 0 為 true,導致驗證繞過:
$_GET['key'] = []; // 陣列
$flag = strcmp($secret, $_GET['key']); // 返回 NULL 並報警
if ($flag == 0) { echo "Welcome!"; } // 實際執行
修補: 使用 hash_equals() 或恆等比較 === 0。
| 運算元 1 類型 | 運算元 2 類型 | 轉換規則 |
|---|---|---|
| null 或 string | string | NULL → "",進行數字/詞彙比較 |
| bool 或 null | 其它 | 轉為 bool,FALSE < TRUE |
| object | object | 同類比較屬性,不同類不可比 |
| string, resource, number | string, resource, number | 轉為數字,按數學比較 |
| array | array | 成員少者較小;鍵不存在則無法比較 |
| array | 其它 | array 總是較大 |
| object | 其它 | object 總是較大 |
=== / !==。hash_equals()(抗時序攻擊)。filter_var() 強類型驗證,再進入業務邏輯。理解 PHP 類型轉換的「隱式規則」,並在代碼規範中強制落地「嚴格比較」,是構建安全應用的基石。