MySQL 是 PHP 與 WordPress 常見資料庫,但『用了 MySQLi/PDO』不等於自動安全。應用程式若將資料串進 SQL、允許 multiple statements、誤用字元集或給帳號過大權限,仍可能發生注入。

本篇會學到什麼
- 理解 MySQL 型別轉換、註解與連線選項對風險判讀的影響
- 正確使用 PDO MySQL native prepared statements
- 為 WordPress/PHP 應用配置最小資料庫權限
必備名詞與心智模型
MySQL server 只看最後收到的 SQL。真正安全的邊界通常由驅動建立:查詢模板與參數分開傳遞。PDO 可使用模擬預處理,這不代表所有用法都危險,但複雜解析與反斜線規則可能有差異;新程式宜關閉模擬並遵守每個值使用獨立 placeholder。
發生情境與生活化比喻
商品分類頁用 MySQL 查詢 category、price 與排序。常見誤區包括把數字不加引號就直接拼接、用 addslashes 代替參數化、忘記明確設定 utf8mb4、以及讓網站帳號持有 CREATE USER 或全域權限。
不安全的原始碼
$min = $_GET['min'] ?? 0;
$category = $_GET['category'] ?? '';
$sql = "SELECT id,name,price FROM products WHERE price >= $min "
. "AND category='$category'";
$rows = $pdo->query($sql)->fetchAll();
此片段刻意省略連線、路由與部署設定,只用來辨識資料流,請勿放入任何服務。
逐步看懂資料如何變成查詢
- min 即使畫面是數字,HTTP 傳入仍是可修改字串
- category 位於單引號 literal,手動 escaping 依賴正確字元集與所有路徑一致
- MySQL 解析完整查詢並套用自己的型別轉換
- 網站帳號權限決定漏洞最多能讀寫什麼
- 錯誤模式與慢查詢日誌可能暴露或偵測異常
修補後原始碼
$pdo = new PDO($dsn, $user, $password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
]);
$min = filter_var($_GET['min'] ?? 0, FILTER_VALIDATE_FLOAT);
$stmt = $pdo->prepare('SELECT id,name,price FROM products
WHERE price >= :min AND category = :category');
$stmt->execute(['min'=>$min, 'category'=>$_GET['category'] ?? '']);
修補的共同原則是先固定 SQL 結構,再把值獨立綁定;無法作為值綁定的識別字只能從程式內允許清單產生。
如何驗證修補
- 確認連線 DSN 明確使用 utf8mb4
- PDO 模擬預處理設定符合團隊決策並有整合測試
- 數字的空值、科學記號、負數與上限有業務規則
- 網站帳號只有需要的 schema/table 權限
- 一般引號與多語言分類名稱保持可用
閱讀提醒:以下分析的目的,是讓你能在自己的程式、測試資料與授權環境中辨識風險。面對正式系統時,先取得明確授權並保存變更紀錄;不要用錯誤訊息或一次回應就推論資料外洩,也不要把文章中的概念改造成自動化探測。安全工作的完成條件,是能說明根因、修補位置、回歸結果與剩餘風險。
更多應用情境、常見誤判與觀察方法
把原理放回真實開發情境
MySQL 實作同時確認原生預備語句、utf8mb4、嚴格模式與最小權限應用帳號。 實作時不要只盯著畫面上的輸入框,還要檢查 API 參數、伺服器端預設值、資料轉換與最後呼叫的資料庫介面。最實用的閱讀方式,是把每一段程式標成「外部來源、轉換、控制、資料庫 sink」,確認資料值沒有在途中重新變回 SQL 結構。
常見誤判
更換資料庫引擎、啟用 WAF 或套用 addslashes 都不會自動消除應用程式的動態 SQL。 安全判斷不能只靠畫面、單次錯誤或某一個防護產品;應同時看原始碼、驅動程式實際執行方式、資料庫權限與回歸測試。若無法證明查詢結構固定,就應把它視為待修的設計風險。
開發與 SOC 可以觀察什麼
追蹤語法錯誤、截斷警告、權限拒絕與慢查詢;管理帳號不可作為網站日常連線帳號。 日誌宜記錄時間、路由、請求關聯 ID、查詢名稱、結果狀態與耗時,敏感值則遮罩或雜湊。偵測規則的用途是縮短發現時間;阻擋之後仍須回到程式碼修補,並搜尋其他共用元件與相同資料流。
審查時的三個追問
- 這個值最早由誰控制,經過哪些轉換?
- 它在資料庫呼叫中是資料值,還是欄位、排序、運算式等結構?
- 若預防失效,最小權限、監控與事件流程能限制並發現多少影響?
開發與維運防禦清單
- 不以 magic quotes、addslashes 或 WAF 取代 bind parameter
- 關閉不需要的 multiple statements 能力
- 依讀寫工作拆分帳號並限制 GRANT
- 監控 MySQL error log、slow query 與大量相似失敗
重點整理與下一篇
MySQL 的 placeholder 與型別行為不能直接套用到所有資料庫。下一篇比較 SQL Server 的 T-SQL、批次語句、sp_executesql 與 ADO.NET 參數。
參考資料與更新日期
更新日期:2026-07-20














