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

PHP 防 SQL 注入實戰:從轉義函數到預處理語句的進階防禦體系

     編輯:PHP 綜合知識

前言

SQL 注入仍是 Web 應用頭號安全風險。本文從注入成因剖析,比較 addslashes、mysql_real_escape_string 與 PDO 預處理語句的防禦效能,建構現代 PHP 專案的標準防注入體系。

一、注入成因還原

$id = $_GET['id']; // 假設攻擊者傳入:1' OR 1=1 #
$sql = "DELETE FROM users WHERE id = '$id'";
// 實際執行:DELETE FROM users WHERE id = '1' OR 1=1 #'

單引號 ' 打破了資料與程式碼邊界,導致邏輯被竄改。

二、傳統轉義函數比較

函數優點缺點適用場景
addslashes()無需資料庫連線不處理字元集、多位元組字元繞過風險極簡腳本、無 DB 連線環境
mysql_real_escape_string()感知連線字元集需有效連線;PHP 7.0 已移除(mysql_* 擴展棄用)舊專案維護(PHP 5.6 及以下)
mysqli_real_escape_string()mysqli 版本,支援字元集仍屬手動轉義,易漏寫mysqli 程式碼庫
// 強制加引號包裹的封裝範例(mysqli)
function quote($str, $link) {
    return "'" . mysqli_real_escape_string($link, $str) . "'";
}

三、現代標準:PDO 預處理語句

預處理(Prepared Statement)將 SQL 結構與資料分離,資料庫驅動層編譯語法樹後再綁定參數,從根本杜絕注入。

$pdo = new PDO($dsn, $user, $pass);
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id AND status = ?");
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->bindValue(1, $status, PDO::PARAM_STR);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

核心優勢:

  • SQL 結構編譯階段不含使用者資料,注入無從著手。
  • 重複執行同結構 SQL 時,編譯結果可複用,效能提升。
  • 自動處理型別轉換與引號包裹。

四、PDO 進階技巧

1. 事務控制

$pdo->beginTransaction();
try {
    $pdo->exec("INSERT ...");
    $pdo->exec("UPDATE ...");
    $pdo->commit();
} catch (Exception $e) {
    $pdo->rollBack();
}

2. 取得最後插入 ID

$id = $pdo->lastInsertId();

3. 防注入輔助:PDO::quote()

若必須拼接 SQL(如動態 ORDER BY),可用 $pdo->quote($val) 自動轉義加引號。

五、單例 PDO 封裝範例

class PDODB {
    private static $instance;
    private $pdo;
    private function __construct(array $cfg) {
        $dsn = "mysql:host={$cfg['host']};port={$cfg['port']};dbname={$cfg['dbname']};charset={$cfg['charset']}";
        $this->pdo = new PDO($dsn, $cfg['user'], $cfg['pass'], [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES {$cfg['charset']}"
        ]);
    }
    public static function getInstance(array $cfg): self {
        return self::$instance ??= new self($cfg);
    }
    public function query(string $sql): PDOStatement {
        return $this->pdo->query($sql);
    }
    public function fetchAll(string $sql): array {
        return $this->query($sql)->fetchAll();
    }
    public function fetchRow(string $sql): ?array {
        return $this->query($sql)->fetch() ?: null;
    }
}

結語

棄用手動轉義,全面採用 PDO 預處理語句是現代 PHP 專案防 SQL 注入的鐵律。搭配嚴格的型別提示、輸入驗證與最小權限資料庫帳號,可構建多層次防禦體系。

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