PHP 允許通過 URL 參數直接提交陣列,例如:
login.php?username=admin&passwd[$ne]=1
後端 $_GET['passwd'] 將得到:
array(1) { ["$ne"]=> string(1) "1" }
若後端直接將用戶輸入拼入 MongoDB 查詢:
$collection->find([
'username' => 'admin',
'passwd' => $_GET['passwd'] // 未過濾,直接傳入
]);
攻擊者提交 passwd[$ne]=1,實際執行的查詢變為:
db.collection.find({
username: 'admin',
passwd: { $ne: 1 } // 「不等於 1」恆為真,繞過密碼驗證
})
若同時注入 username[$ne]=1&passwd[$ne]=1,可遍歷所有用戶記錄。
<?php
// 測試參數接收
$passwd = $_GET['passwd'];
var_dump($passwd);
// 訪問 ?passwd[$ne]=1 輸出:
// array(1) { ["$ne"]=> string(1) "1" }
在構建查詢前,將所有用戶輸入強制轉為預期的標量類型:
$collection->find([
'username' => (string)$_GET['username'],
'passwd' => (string)$_GET['passwd']
]);
轉為字串後,$ne 等操作符失去作用,查詢退化為精確匹配。
$ 開頭鍵的陣列。MongoDB\Driver\Query、MongoDB\Model\BSONDocument 或 Laravel MongoDB ODM,避免手動拼裝陣列。$_GET、$_POST、$_REQUEST 中的陣列鍵名,移除或轉義 $ 開頭鍵。admin 或 root。原理與經典 SQL 注入相似:
-- SQL 注入
SELECT * FROM users WHERE username='admin' AND passwd!='1';
-- MongoDB 注入
db.users.find({ username: 'admin', passwd: { $ne: 1 } })
核心都是**未經驗證的用戶輸入直接進入查詢語句構建**,導致邏輯被竄改。