SQL 注入是 Web 應用中最常見也最危險的安全漏洞之一。PHP 提供了多種函數來轉義輸入數據,但並非所有函數都同樣可靠。本文將深入分析 addslashes、mysql_real_escape_string、mysql_escape_string 的區別,並給出現代 PHP 開發中推薦的防注入方案。
早期許多 PHP 程式碼依賴 addslashes() 來防止 SQL 注入。然而,這個函數僅針對單字節字符串有效,在多字節編碼(如 GBK、Big5)環境下存在繞過風險。
繞過原理: 攻擊者可使用 0xbf27 代替單引號(0x27)。addslashes() 會將其轉為 0xbf5c27,但數據庫在解析多字節字符時,0xbf5c 會被視為一個合法字符,導致後面的單引號「倖存」,從而構成注入。
結論:
addslashes()只適合單字節字符集(如 Latin1),不建議 用於中文等多字節環境。
mysql_real_escape_string(string $unescaped_string, resource $link_identifier = null) 會根據當前連接的字符集正確轉義特殊字符,是防注入的標準選擇。
% 和 _(LIKE 查詢通配符),如需防禦需額外處理。<?php
$item = "Zak's and Derick's Laptop";
$escaped_item = mysql_real_escape_string($item);
printf("Escaped string: %s\n", $escaped_item);
// 輸出: Escaped string: Zak\'s and Derick\'s Laptop
?>
功能與 mysql_real_escape_string() 類似,但不接受連接資源,也不考慮當前字符集。PHP 4.3.0 之前版本只能用此函數,現已棄用,請勿在新項目中使用。
舊版 PHP 可能開啟 magic_quotes_gpc 自動轉義 GET/POST/COOKIE 數據。為避免雙重轉義,建議採用如下封裝函數:
function clean_input($dirty) {
if (get_magic_quotes_gpc()) {
$clean = mysql_real_escape_string(stripslashes($dirty));
} else {
$clean = mysql_real_escape_string($dirty);
}
return $clean;
}
自 PHP 5.1 起,PDO 和 mysqli 都支援預處理語句,這是防止 SQL 注入的根本解決方案。參數化查詢將數據與 SQL 邏輯徹底分離,無需手動轉義。
// PDO 範例
$stmt = $pdo->prepare('SELECT * FROM users WHERE username = :name');
$stmt->execute([':name' => $username]);
$row = $stmt->fetch();
| 函數 | 字符集感知 | PHP 版本要求 | 推薦程度 |
|---|---|---|---|
| addslashes() | 否 | 所有版本 | ❌ 不推薦 |
| mysql_escape_string() | 否 | PHP 4.3.0 前 | ❌ 已棄用 |
| mysql_real_escape_string() | 是 | PHP 4.3.0+ | ⚠️ 僅限舊項目維護 |
| PDO / mysqli 預處理 | 是 | PHP 5.1+ | ✅ 強烈推薦 |
在新專案中,請直接使用 PDO 或 mysqli 的預處理語句;維護老代碼時,務必用 mysql_real_escape_string() 並注意字符集設置。