當使用者輸入未經過濾直接拼接進 SQL 語句,攻擊者可注入惡意程式碼竊取、修改或刪除資料。例如:
$unsafe = $_POST['user_input'];
mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe')");
若輸入 value'); DROP TABLE table;--,實際執行的 SQL 變為:
INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')
預處理語句將 SQL 邏輯與資料分離,參數視為純文字,徹底杜絕注入。PHP 提供兩種主流實作:
<?php
// 建立連線並關鍵:關閉模擬預處理,使用真正的預處理
$pdo = new PDO('mysql:host=127.0.0.1;dbname=dbtest;charset=utf8', 'user', 'pass');
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 查詢範例
$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
$stmt->execute(['name' => $name]);
foreach ($stmt as $row) {
// 處理資料
}
// 新增範例
$stmt = $pdo->prepare('INSERT INTO `table` (`column`) VALUES (:col)');
$stmt->execute(['col' => $unsafeValue]);
<?php
$mysqli = new mysqli('127.0.0.1', 'user', 'pass', 'dbtest');
$stmt = $mysqli->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s', $name); // 's' 代表字串
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
// 處理資料
}
預設 PDO 會在 PHP 端模擬預處理,實際仍將參數拼接進 SQL 字串再送給 MySQL,若字元集設定不當仍有風險。設定 ATTR_EMULATE_PREPARES = false 強制使用 MySQL 原生預處理,確保參數在傳輸前不被 PHP 解析。
額外好處:同一連線重複執行相同預處理語句,資料庫只解析一次,效能提升。
%,如 $stmt->execute(['keyword' => "%$kw%"])。ERRMODE_EXCEPTION 並捕獲 PDOException,避免敏感資訊外洩。