使用引數化查詢防止 SQL 注入
SQL 注入是一種攻擊,允許惡意使用者修改 SQL 查詢,向其中新增不需要的命令。例如,以下程式碼易受攻擊 :
// Do not use this vulnerable code!
$sql = 'SELECT name, email, user_level FROM users WHERE userID = ' . $_GET['user'];
$conn->query($sql);
這允許該指令碼的任何使用者基本上隨意修改我們的資料庫。例如,考慮以下查詢字串:
page.php?user=0;%20TRUNCATE%20TABLE%20users;
這使我們的示例查詢看起來像這樣
SELECT name, email, user_level FROM users WHERE userID = 0; TRUNCATE TABLE users;
雖然這是一個極端的例子(大多數 SQL 注入攻擊的目的不是刪除資料,大多數 PHP 查詢執行函式也不支援多查詢),但這是一個示例,說明如何通過粗心組裝來實現 SQL 注入攻擊。查詢。不幸的是,像這樣的攻擊很常見,並且由於編碼人員沒有采取適當的預防措施來保護他們的資料,因此非常有效。
為防止 SQL 注入發生,建議的語句是推薦的解決方案。不是將使用者資料直接連線到查詢,而是使用佔位符。然後單獨傳送資料,這意味著 SQL 引擎不會將使用者資料混淆為一組指令。
PDO 支援兩種佔位符(佔位符不能用於列名或表名,只能用於值):
-
命名佔位符。冒號(
:
),後跟一個不同的名稱(例如:user
)// using named placeholders $sql = 'SELECT name, email, user_level FROM users WHERE userID = :user'; $prep = $conn->prepare($sql); $prep->execute(['user' => $_GET['user']]); // associative array $result = $prep->fetchAll();
-
傳統的 SQL 位置佔位符,表示為
?
:// using question-mark placeholders $sql = 'SELECT name, user_level FROM users WHERE userID = ? AND user_level = ?'; $prep = $conn->prepare($sql); $prep->execute([$_GET['user'], $_GET['user_level']]); // indexed array $result = $prep->fetchAll();
如果你需要動態更改表名或列名,請知道這是你自己的安全風險和不良做法。雖然,它可以通過字串連線來完成。提高此類查詢安全性的一種方法是設定允許值表,並將要連線的值與此表進行比較。
請注意,僅通過 DSN 設定連線字符集很重要,否則如果使用某些奇數編碼,你的應用程式可能容易出現模糊的漏洞 。對於 5.3.6 之前的 PDO 版本,通過 DSN 設定字符集不可用,因此唯一的選擇是在建立後立即在連線上將 PDO::ATTR_EMULATE_PREPARES
屬性設定為 false
。
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
這會導致 PDO 使用底層 DBMS 的本機預處理語句,而不僅僅是模擬它。