PDO (PHP Data Objects) 是 PHP 連接數據庫的標準化接口,正確使用能從根本上阻斷 SQL 注入。本文基於 WireShark 抓包分析,深度剖析 PDO 預處理與 MySQL 服務端的交互細節,揭示 ATTR_EMULATE_PREPARES 陷阱,給出生產環境強制配置清單。
PDOException,便於統一攔截COM_STMT_PREPARE 包含帶佔位符(?)的 SQL 模板statement_id、參數數量、列元數據COM_STMT_EXECUTE 僅攜帶二進制參數值這在協議層面保證了代碼與數據物理隔離,注入不可能發生。
PDO 默認開啟 客戶端模擬預處理:
PDO::quote() 轉義參數,拼接成完整 SQLCOM_QUERY 普通查詢包SET NAMES gbk 繞過)WireShark 實證:開啟模擬模式時,網絡包中可見拼接後的完整 SQL;關閉後僅見二進制參數流。
<?php
$dsn = 'mysql:host=localhost;dbname=mydb;charset=utf8mb4';
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false, // 關鍵:強制真實預處理
PDO::MYSQL_ATTR_SSL_CA => '/path/ca.pem', // 如啟用 SSL
];
$pdo = new PDO($dsn, 'user', 'pass', $options);
charset=utf8mb4(PHP 5.3.6+ 支持),告訴驅動本地轉義用的字符集SET NAMES utf8mb4,確保服務端通信字符集一致$pdo->exec("SET NAMES utf8mb4");
| 框架 | 配置位置 | 關鍵設置 |
|---|---|---|
| Yii 2 | config/db.php | 'attributes' => [PDO::ATTR_EMULATE_PREPARES => false], 'charset' => 'utf8mb4' |
| Laravel | config/database.php | 'options' => [PDO::ATTR_EMULATE_PREPARES => false](默認已關閉) |
| ThinkPHP 6 | config/database.php | 'params' => [PDO::ATTR_EMULATE_PREPARES => false] |
? 或 :name)ATTR_EMULATE_PREPARES = falsecharset=utf8mb4 + 連接後 SET NAMES utf8mb4LIMIT、ORDER BY 動態部分)LIMIT、OFFSET 需在 PHP 端校驗為整數後拼接(或用 LIMIT ?, ? 8.0+ 支持)PDO::quote() 就安全 → 仍屬模擬模式,字符集陷阱依然存在