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

PHP 防 SQL 注入實務:棄用 eregi、採用預處理語句與參數化查詢

     編輯:PHP 相關知識

    ⚠️ 原始碼問題分析

    原文提供的防注入碼存在嚴重缺陷:

    1. 使用已移除函數:eregi() 自 PHP 5.3 棄用、PHP 7.0 移除。
    2. 黑名單過濾不可靠:關鍵字過濾易被繞過(編碼、註釋、大小寫變形、十六進位等)。
    3. 未處理 POST Body:file_get_contents("php://input") 僅能讀取一次,且不涵蓋 multipart/form-data。
    4. 缺乏語境感知:SQL 注入防護必須在「查詢建構」層面解決,而非「輸入」層面。

    現代防 SQL 注入鐵律:參數化查詢 (預處理語句)

    核心原則:代碼與資料分離。SQL 結構在準備階段確定,參數在執行階段綁定,資料庫引擎保證參數永遠不會被解釋為 SQL 指令。

    mysqli 範例

    <?php
    $mysqli = new mysqli('localhost', 'user', 'pass', 'db');
    $mysqli->set_charset('utf8mb4');
    
    $keyword = $_GET['q'] ?? '';
    
    // ✅ 正確:佔位符 ? 對應字串參數
    $stmt = $mysqli->prepare("SELECT * FROM products WHERE name LIKE ? AND status = 1");
    $like = "%{$keyword}%";
    $stmt->bind_param('s', $like);
    $stmt->execute();
    $result = $stmt->get_result();
    
    while ($row = $result->fetch_assoc()) {
        // 輸出...
    }
    $stmt->close();

    PDO 範例 (推薦)

    <?php
    $pdo = new PDO('mysql:host=localhost;dbname=db;charset=utf8mb4', 'user', 'pass', [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_EMULATE_PREPARES => false, // 關鍵:使用真實預處理
    ]);
    
    $keyword = $_GET['q'] ?? '';
    
    // ✅ 正確:陣列傳參自動綁定
    $stmt = $pdo->prepare("SELECT * FROM products WHERE name LIKE ? AND status = 1");
    $stmt->execute(["%{$keyword}%"]);
    
    foreach ($stmt as $row) {
        // 輸出...
    }

    何時無法使用預處理?替代方案

    場景解決方案範例
    動態欄位名/表名白名單驗證if (!in_array($col, $allowedCols)) throw new Exception();
    動態 ORDER BY / LIMIT白名單 + 型別轉換ORDER BY {$allowed[$col]} LIMIT " . (int)$limit
    IN 子句動態參數數量動態產生佔位符$in = implode(',', array_fill(0, count($ids), '?'));
    舊專案無法重構專用逃逸函數 (次佳)$mysqli->real_escape_string($val) / $pdo->quote($val)

    IN 子句動態參數實作

    <?php
    $ids = [1, 5, 9, 20]; // 來自前端陣列
    
    // 動態產生 ?,?,?,?
    $placeholders = implode(',', array_fill(0, count($ids), '?'));
    $sql = "SELECT * FROM users WHERE id IN ($placeholders)";
    
    $stmt = $pdo->prepare($sql);
    $stmt->execute($ids); // 陣列直接對應佔位符
    
    $users = $stmt->fetchAll();

    多層防護策略 (纏繞防禦)

    1. 資料庫層:唯讀帳號、最小權限、預處理語句 (核心)。
    2. 應用層:輸入驗證 (型別、長度、格式)、輸出編碼 (HTML/JS/URL)。
    3. 框架層:使用 ORM/Query Builder (Laravel Eloquent, ThinkPHP Db::table, Yii ActiveRecord) 內建參數化。
    4. 基礎設施層:WAF (ModSecurity)、資料庫審計日誌、錯誤訊息不外洩。

    快速自檢清單

    • [ ] 專案中零 mysql_* / ereg* 函數
    • [ ] 所有動態 SQL 使用 prepare() + bind_param/execute([])
    • [ ] 無字串拼接 SQL:"WHERE id = $id" ❌ → "WHERE id = ?" ✅
    • [ ] 錯誤模式設為例外:mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT) / PDO::ERRMODE_EXCEPTION
    • [ ] 正式環境 display_errors = Off,log_errors = On

    徹底貫徹「參數化查詢」原則,才是防禦 SQL 注入的根本之道。黑名單過濾只是假象,請立即移除。

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