「有過濾」、「欄位是數字」或「已經用了 PDO」都不等於查詢安全。真正要確認的是外部資料在資料庫解析 SQL 之前,能不能改變語法結構。

本篇會學到什麼
- 字串拼接、型別假設與信任邊界如何一起造成漏洞。
- 值 placeholder 為何不能代替欄位名。
- 參數化、允許清單與最小權限各自解決什麼問題。
必備名詞與心智模型
Source 是外部資料來源,transform 是解碼、轉型或拼接,sink 是執行 SQL 的 API。Taint tracking 的核心問題是:不可信資料是否在沒有安全邊界的情況下抵達 sink。數字欄位也可能被直接修改;只有伺服器端明確轉型與範圍檢查才有意義。
發生情境與生活化比喻
字串拼接像把旅客的留言直接印在登機指令上;參數化像把留言放進封好的行李欄位。排序欄位則像選航線,不能把任意文字當行李處理,必須讓旅客從既定航線代碼中選擇。
不安全的原始碼
$category = $_GET['category'] ?? '';
$sort = $_GET['sort'] ?? 'name';
$sql = "SELECT id,name,price FROM products "
. "WHERE category='" . $category . "' ORDER BY " . $sort;
$category 位於資料值,$sort 位於 SQL 結構;兩者需要不同處理。只對 category 做 escaping 仍留下 sort 風險。只在前端提供下拉選單也不安全,因為請求可被直接修改。
逐步看懂資料如何變成查詢
- 來源:兩個 GET 參數。
- 轉換:沒有伺服器端型別或 mapping。
- sink:query 執行完整字串。
- category 應變成參數值。
- sort 應先映射到程式內固定片段。
- 再檢查連線帳號是否只有 SELECT 權限。
修補後原始碼
$allowedSorts = [
'name' => 'name ASC',
'price_asc' => 'price ASC',
'price_desc' => 'price DESC',
];
$orderBy = $allowedSorts[$_GET['sort'] ?? 'name'] ?? $allowedSorts['name'];
$statement = $pdo->prepare(
"SELECT id,name,price FROM products WHERE category=:category ORDER BY {$orderBy}"
);
$statement->execute(['category' => $_GET['category'] ?? '']);
這裡的動態片段只可能來自程式碼內三個固定值。PDO 文件也指出 placeholder 代表完整資料 literal,不能拿來綁定關鍵字、欄位名或任意查詢片段。
如何驗證修補
- 三個合法排序代碼得到預期順序。
- 未知、空白與大小寫變體回到安全預設。
- category 中的引號、Unicode 與長字串只作為值。
- 程式碼掃描找不到 request 值直接進入 SQL 插值。
- 資料庫帳號無建表、刪表與管理權限。
閱讀提醒:以下分析的目的,是讓你能在自己的程式、測試資料與授權環境中辨識風險。面對正式系統時,先取得明確授權並保存變更紀錄;不要用錯誤訊息或一次回應就推論資料外洩,也不要把文章中的概念改造成自動化探測。安全工作的完成條件,是能說明根因、修補位置、回歸結果與剩餘風險。
更多應用情境、常見誤判與觀察方法
把原理放回真實開發情境
姓名與搜尋詞是資料值;欄位名、排序方向和資料表名屬於 SQL 結構,兩者需要不同控制。 實作時不要只盯著畫面上的輸入框,還要檢查 API 參數、伺服器端預設值、資料轉換與最後呼叫的資料庫介面。最實用的閱讀方式,是把每一段程式標成「外部來源、轉換、控制、資料庫 sink」,確認資料值沒有在途中重新變回 SQL 結構。
常見誤判
只做跳脫或只檢查單引號,會忽略數字、編碼、動態識別字與二次使用。 安全判斷不能只靠畫面、單次錯誤或某一個防護產品;應同時看原始碼、驅動程式實際執行方式、資料庫權限與回歸測試。若無法證明查詢結構固定,就應把它視為待修的設計風險。
開發與 SOC 可以觀察什麼
Code review 應由資料來源一路追到 query API,並標記每一次字串串接與型別轉換。 日誌宜記錄時間、路由、請求關聯 ID、查詢名稱、結果狀態與耗時,敏感值則遮罩或雜湊。偵測規則的用途是縮短發現時間;阻擋之後仍須回到程式碼修補,並搜尋其他共用元件與相同資料流。
審查時的三個追問
- 這個值最早由誰控制,經過哪些轉換?
- 它在資料庫呼叫中是資料值,還是欄位、排序、運算式等結構?
- 若預防失效,最小權限、監控與事件流程能限制並發現多少影響?
開發與維運防禦清單
- 禁止用黑名單猜測危險字元。
- 同一資料只正規化一次,再做型別與業務規則驗證。
- 程式碼審查搜尋
query()、raw SQL、字串插值與 stored procedure 內的動態 SQL。 - 記錄拒絕原因與事件 ID,不把 SQL 原文回傳前端。
重點整理與下一篇
值、識別字與 SQL 片段不是同一種東西。值使用參數化,有限選項使用允許清單,權限限制漏洞發生後的影響。下一篇將用登入框說明查詢邏輯與身分驗證為何特別敏感。
參考資料與更新日期
更新日期:2026-07-20











