// 正常預期
$id = 3;
$sql = "DELETE FROM news WHERE id = $id"; // DELETE FROM news WHERE id = 3
// 惡意構造
$id = "3 OR 1";
$sql = "DELETE FROM news WHERE id = $id"; // DELETE FROM news WHERE id = 3 OR 1 → 全表刪除
鐵律:永遠不信任客戶端輸入 ($_GET/$_POST/$_COOKIE/$_REQUEST/$_SERVER)。
foreach ($_POST as $k => $v) {
if (is_string($v)) $_POST[$k] = addslashes($v);
}
function _addslashes(&$value, $key) {
$value = addslashes($value);
}
array_walk_recursive($_POST, '_addslashes');
array_walk_recursive($_GET, '_addslashes');
array_walk_recursive($_COOKIE, '_addslashes');
// php.ini: magic_quotes_gpc = On
// 自動對 GPC 數據 addslashes
// 兼容代碼:防止雙重轉義
if (!get_magic_quotes_gpc()) {
array_walk_recursive($_POST, '_addslashes');
array_walk_recursive($_GET, '_addslashes');
array_walk_recursive($_COOKIE, '_addslashes');
}
轉義本質是字符串拼接的補丁,預處理從根源分離代碼與數據。
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false, // 使用真實預處理
]);
// 參數綁定
$stmt = $pdo->prepare('DELETE FROM news WHERE id = ?');
$stmt->execute([$_GET['id']]); // 自動處理類型、轉義、防注入
// 命名佔位符
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email AND status = :status');
$stmt->execute(['email' => $email, 'status' => 1]);
<?php
$mysqli = new mysqli('localhost', 'user', 'pass', 'test');
$stmt = $mysqli->prepare('DELETE FROM news WHERE id = ?');
$stmt->bind_param('i', $_GET['id']); // i=integer, s=string, d=double, b=blob
$stmt->execute();
display_errors,記錄日誌而非輸出頁面。ctype_digit()、(int)$_GET['id']、filter_var($id, FILTER_VALIDATE_INT)。從 addslashes 到 Magic Quotes 再到預處理語句,PHP 防注入歷程是從「字符串修補」走向「代碼數據分離」。新項目無理由不使用 PDO/MySQLi 預處理,舊項目重構優先級最高。