早期 PHP 專案常在入口文件對 $_GET、$_POST、$_COOKIE 進行全局 addslashes。本文分析此模式的局限性,並給出生產環境推薦的分層防護架構。
<?php
if (!get_magic_quotes_gpc()) {
$_GET = addslashes_deep($_GET);
$_POST = addslashes_deep($_POST);
$_COOKIE = addslashes_deep($_COOKIE);
$_REQUEST= addslashes_deep($_REQUEST);
}
function addslashes_deep($value) {
if (empty($value)) return $value;
return is_array($value)
? array_map('addslashes_deep', $value)
: addslashes($value);
}
?>
缺陷:
addslashes 僅轉義單雙引號、反斜槓、NULL,無法防禦編碼繞過、寬字節注入;stripslashes,易出現雙重轉義或漏轉;$_GET['key\' OR 1=1']);magic_quotes_gpc,此模式失去基礎。<?php
// 使用 filter_var / filter_input 進行類型與格式驗證
$page = filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT, ['options' => ['default' => 1, 'min_range' => 1]]);
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
$name = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_STRING); // 移除標籤
// 自定義驗證器類(推薦)
class Validator {
public static function int($val, $default = 0, $min = null, $max = null) {
$val = filter_var($val, FILTER_VALIDATE_INT, ['options' => ['min_range' => $min, 'max_range' => $max]]);
return $val !== false ? $val : $default;
}
public static function str($val, $maxLen = 255, $allowHtml = false) {
$val = $allowHtml ? $val : strip_tags($val);
return mb_substr($val, 0, $maxLen, 'UTF-8');
}
}
$page = Validator::int($_GET['page'] ?? 1, 1, 1);
$username = Validator::str($_POST['username'] ?? '', 50);
唯一有效防注入手段:預處理語句。
<?php
// PDO 範例
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email AND status = :status');
$stmt->execute([':email' => $email, ':status' => 1]);
// MySQLi 範例
$stmt = $mysqli->prepare('SELECT * FROM users WHERE email = ? AND status = ?');
$stmt->bind_param('si', $email, $status);
$stmt->execute();
預處理將 SQL 邏輯與數據徹底分離,數據永遠不會被解釋為 SQL 指令。
<?php
// HTML 上下文
echo htmlspecialchars($userInput, ENT_QUOTES | ENT_HTML5, 'UTF-8');
// JavaScript 上下文
echo json_encode($userInput); // 自動處理引號、換行、Unicode
// URL 參數
echo urlencode($userInput);
// Shell 命令(極少用,慎用)
echo escapeshellarg($userInput);
auto_filter 配置、預處理查詢構建器;{{ }} 轉義;現代框架已在架構層面解決注入問題,業務代碼應避免手寫 SQL,優先使用 ORM/Query Builder。
<?php
// 危險:直接遍歷 $_GET 鍵名構建 SQL
foreach ($_GET as $key => $value) {
$sql .= "`$key` = '$value', "; // $key 可注入!
}
// 安全:白名單字段
$allowed = ['name', 'email', 'age'];
foreach ($allowed as $field) {
if (isset($_GET[$field])) {
$data[$field] = $_GET[$field];
}
}
$stmt = $pdo->prepare('UPDATE users SET name=:name, email=:email, age=:age WHERE id=:id');
$stmt->execute(array_merge($data, ['id' => $uid]));
全局 addslashes 是上世紀的產物。現代 PHP 防注入鐵律:
配合資料庫最小權限賬號、WAF、SQL 審計日誌,構建纵深防禦體系。