SQL 注入仍是 Web 應用最常見的高危漏洞。根本對策只有一個——永遠使用預處理語句與參數化查詢,絕不拼接字串。本文以 PDO 與 mysqli 為例,示範正確用法與關鍵設定。
當使用者輸入 value'); DROP TABLE users;-- 並直接拼接進 SQL:
$sql = "INSERT INTO log (msg) VALUES ('" . $_POST['msg'] . "')";
// 實際執行變成:
// INSERT INTO log (msg) VALUES ('value'); DROP TABLE users;--')
資料庫會執行兩條語句,造成災難性後果。
<?php
// 1. 建立連線並關鍵:關閉模擬預處理
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8', 'user', 'pass');
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); // 必須!使用真正預處理
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 2. 準備語句(命名佔位符)
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute([':email' => $userInput]);
// 3. 取得結果
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
// 處理資料
}
<?php
$mysqli = new mysqli('127.0.0.1', 'user', 'pass', 'test');
$stmt = $mysqli->prepare('SELECT * FROM users WHERE email = ?');
$stmt->bind_param('s', $userInput); // 's' 表字串,'i' 整數, 'd' 雙精度, 'b' Blob
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) { /* ... */ }
bind_param 強制指定型別,進一步消除注入面。PDO::quote() 或 mysqli_real_escape_string() 仍屬「過濾法」,無法覆蓋所有邊緣情況(如字元集陷阱)。SET NAMES utf8。結論:全專案統一使用預處理語句,是唯一可靠、可維護的防注入基線。