首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP $_GET $_POST 全局過濾與 SQL 注入防護最佳實踐

     編輯:PHP 相關知識

全局輸入過濾:從 addslashes 到現代防護體系

早期 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']);
  • PHP 5.4+ 移除 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 指令。

第三層:輸出編碼(防 XSS 最後一道防線)

<?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);

框架級全局防護(ThinkPHP / Laravel 等)

  • ThinkPHP:模型自動驗證、auto_filter 配置、預處理查詢構建器;
  • Laravel:Eloquent ORM 內建預處理、表單請求驗證、Blade 模板自動 {{ }} 轉義;
  • Yii2:ActiveRecord 參數綁定、輸入驗證器、輸出編碼助手。

現代框架已在架構層面解決注入問題,業務代碼應避免手寫 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 防注入鐵律:

  1. 業務層:嚴格驗證類型、長度、格式,拒絕非預期輸入;
  2. 數據層:100% 預處理語句,零手寫拼接 SQL;
  3. 展示層:上下文感知編碼(HTML/JS/URL/Shell),框架模板引擎自動處理。

配合資料庫最小權限賬號、WAF、SQL 審計日誌,構建纵深防禦體系。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved