如何防止 PHP 中進行 SQL 注入
如果將用戶輸入未經修改地插入到SQL查詢中,則該應用程序容易受到SQL注入的攻擊,如以下示例所示:
$unsafe_variable = $_POST['user_input'];
mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");
這是因為用戶可以輸入類似的內容value'); DROP TABLE table;--,并且查詢變為:
INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')
可以采取什么措施防止這種情況發生?
使用準備好的語句和參數化查詢。這些是獨立于任何參數發送到數據庫服務器并由數據庫服務器解析的SQL語句。這樣,攻擊者就不可能注入惡意SQL。
您基本上有兩種選擇可以實現此目的:
1.使用PDO(對于任何受支持的數據庫驅動程序):
2.使用MySQLi(對于MySQL):
如果你連接到MySQL之外的數據庫,有一個特定的驅動程序,第二個選項,你可以參考一下(例如,
pg_prepare()和pg_execute()PostgreSQL的)。PDO是通用選項。正確設置連接
請注意,PDO用于訪問MySQL數據庫時,默認情況下不使用真實的預處理語句。要解決此問題,您必須禁用對準備好的語句的仿真。使用PDO創建連接的示例如下:
在上面的示例中,錯誤模式不是嚴格必需的,但建議添加它。這樣,
Fatal Error當出現問題時腳本不會以a停止。并且它為開發人員提供了解決catch任何thrown為PDOExceptions的錯誤的機會。但是,第一行是強制性的,
setAttribute()它告訴PDO禁用模擬的預處理語句并使用實際的預處理語句。這樣可以確保在將語句和值發送到MySQL服務器之前,不會對PHP進行語法分析(這使可能的攻擊者沒有機會注入惡意SQL)。盡管可以
charset在構造函數的選項中設置,但是必須注意,PHP的“較舊”版本(在5.3.6之前)靜默忽略了DSN中的charset參數。說明
傳遞給您的SQL語句
prepare由數據庫服務器解析和編譯。通過指定參數(如上例中的?參數或命名參數:name),您可以告訴數據庫引擎要在何處進行過濾。然后,當您調用時execute,準備好的語句將與您指定的參數值組合在一起。這里重要的是參數值與已編譯的語句(而不是SQL字符串)組合在一起。SQL注入通過在創建要發送到數據庫的SQL時欺騙腳本使其包含惡意字符串來起作用。因此,通過將實際的SQL與參數分開發送,可以減少因意外獲得最終結果的風險。
使用預處理語句發送的任何參數都將被視為字符串(盡管數據庫引擎可能會進行一些優化,因此參數最終也可能以數字結尾)。在上面的示例中,如果
$name變量包含'Sarah';DELETE FROM employees結果,則僅是搜索字符串"'Sarah'; DELETE FROM employees",并且最終不會得到空表。使用準備好的語句的另一個好處是,如果您在同一會話中多次執行同一條語句,則該語句僅被解析和編譯一次,從而可以提高速度。
哦,既然您詢問了如何進行插入,這是一個示例(使用PDO):
準備好的語句可以用于動態查詢嗎?
盡管您仍可以對查詢參數使用準備好的語句,但是無法對動態查詢本身的結構進行參數化,并且無法對某些查詢功能進行參數化。
對于這些特定方案,最好的辦法是使用白名單過濾器來限制可能的值。
回答所涉及的環境:聯想天逸510S、Windows 10。