如果用戶輸入未經(jīng)修改就插入到 SQL 查詢中,則應(yīng)用程序容易受到SQL 注入攻擊,如以下示例所示:
$unsafe_variable = $_POST['user_input']; mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");
那是因為用戶可以輸入類似 的內(nèi)容 value'); DROP TABLE table;--,查詢變?yōu)椋?/p>
INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')
做些什么來防止這種情況發(fā)生?防止這些Sql注入?
解決方案
無論使用哪種數(shù)據(jù)庫,避免 SQL 注入攻擊的正確方法是將數(shù)據(jù)與 SQL 分離,讓數(shù)據(jù)保持?jǐn)?shù)據(jù)狀態(tài),永遠(yuǎn)不會被SQL 解析器解釋為命令??梢允褂酶袷秸_的數(shù)據(jù)部分創(chuàng)建 SQL 語句,但如果您不完全理解細(xì)節(jié),則應(yīng)始終使用準(zhǔn)備好的語句和參數(shù)化查詢。這些是與任何參數(shù)分開發(fā)送到數(shù)據(jù)庫服務(wù)器并由其解析的 SQL 語句。這樣攻擊者就不可能注入惡意 SQL。
您基本上有兩種選擇來實現(xiàn)這一目標(biāo):
1、使用PDO(對于任何支持的數(shù)據(jù)庫驅(qū)動程序):
$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name'); $stmt->execute([ 'name' => $name ]); foreach ($stmt as $row) { // Do something with $row }
2、使用MySQLi(對于 MySQL):
從 PHP 8.2+ 開始,我們可以使用 execute_query() which 在一個方法中準(zhǔn)備、綁定參數(shù)和執(zhí)行 SQL 語句:
$result = $dbConnection->execute_query('SELECT * FROM employees WHERE name = ?', [$name]); while ($row = $result->fetch_assoc()) { // Do something with $row }
至 PHP8.1:
$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?'); $stmt->bind_param('s', $name); // 's' specifies the variable type => 'string' $stmt->execute(); $result = $stmt->get_result(); while ($row = $result->fetch_assoc()) { // Do something with $row }
如果您要連接到 MySQL 以外的數(shù)據(jù)庫,則可以參考特定于驅(qū)動程序的第二個選項(例如,pg_prepare() 對于 pg_execute() PostgreSQL)。PDO 是通用選項。
正確設(shè)置連接
PDO
請注意,當(dāng)使用PDO訪問 MySQL 數(shù)據(jù)庫時,默認(rèn)情況下不會使用真正的準(zhǔn)備好的語句。要解決此問題,您必須禁用準(zhǔn)備好的語句的模擬。使用PDO創(chuàng)建連接的示例是:
$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8mb4', 'user', 'password'); $dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
在上面的例子中,error mode 并不是絕對必要的,但建議添加它。這樣 PDO 將通過拋出 PDOException.
然而,強制性的是第一 setAttribute() 行,它告訴 PDO 禁用模擬的準(zhǔn)備好的語句并使用真正的準(zhǔn)備好的語句。
這確保語句和值在將其發(fā)送到 MySQL 服務(wù)器之前不會被 PHP 解析(使可能的攻擊者沒有機會注入惡意 SQL)。
盡管您可以在構(gòu)造函數(shù)的選項中設(shè)置charset,但請務(wù)必注意“舊”版本的 PHP(5.3.6 之前)會自動忽略DSN 中的字符集參數(shù)。
mysqli
對于 mysqli,我們必須遵循相同的例程:
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); // error reporting $dbConnection = new mysqli('127.0.0.1', 'username', 'password', 'test'); $dbConnection->set_charset('utf8mb4'); // charset
解釋
您傳遞給的 SQL 語句prepare由數(shù)據(jù)庫服務(wù)器解析和編譯。通過指定參數(shù)(如上例中的一個?或命名參數(shù)),您可以告訴數(shù)據(jù)庫引擎您要過濾的位置。:name然后,當(dāng)您調(diào)用 時execute,準(zhǔn)備好的語句將與您指定的參數(shù)值組合。
這里重要的是參數(shù)值與編譯語句組合在一起,而不是 SQL 字符串。SQL 注入的工作原理是在創(chuàng)建要發(fā)送到數(shù)據(jù)庫的 SQL 時誘使腳本包含惡意字符串。因此,通過將實際 SQL 與參數(shù)分開發(fā)送,您可以限制以非預(yù)期結(jié)果結(jié)束的風(fēng)險。
您在使用準(zhǔn)備好的語句時發(fā)送的任何參數(shù)都將被視為字符串(當(dāng)然,盡管數(shù)據(jù)庫引擎可能會進(jìn)行一些優(yōu)化,因此參數(shù)也可能最終以數(shù)字形式結(jié)束)。在上面的示例中,如果$name變量包含'Sarah'; DELETE FROM employees結(jié)果將只是對字符串的搜索"'Sarah'; DELETE FROM employees",而不會以空表結(jié)束。
使用準(zhǔn)備好的語句的另一個好處是,如果您在同一個會話中多次執(zhí)行同一個語句,它只會被解析和編譯一次,從而提高速度。
哦,既然你問過如何插入,這里有一個例子(使用 PDO):
$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)'); $preparedStatement->execute([ 'column' => $unsafeValue ]);
準(zhǔn)備好的語句可以用于動態(tài)查詢嗎?
雖然您仍然可以為查詢參數(shù)使用準(zhǔn)備好的語句,但動態(tài)查詢本身的結(jié)構(gòu)無法參數(shù)化,某些查詢功能也無法參數(shù)化。
對于這些特定場景,最好的辦法是使用限制可能值的白名單過濾器。
// Value whitelist // $dir can only be 'DESC', otherwise it will be 'ASC' if (empty($dir) || $dir !== 'DESC') { $dir = 'ASC'; }
文章來源地址http://www.zghlxwxcb.cn/article/260.html
文章來源:http://www.zghlxwxcb.cn/article/260.html
到此這篇關(guān)于如何防止PHP中的 SQL 注入?的文章就介紹到這了,更多相關(guān)內(nèi)容可以在右上角搜索或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!