歡迎光臨
我們一直在努力

用了 PDO、Prepared Statement 或 ORM 為何仍有漏洞

安全提醒:本文為防禦導向的虛構案例與不可部署片段;測試僅限自有或明確授權環境。

安全工具只能保護正確使用的範圍。PDO prepare 不能綁定欄位名,ORM 的 raw() 或 whereRaw() 仍會接受 SQL,stored procedure 內也可能再次拼接。程式碼審查要看完整資料流,不能只搜尋 prepare 這個字。

ORM 的安全邊界技術流程圖
ORM 的安全邊界:依序追蹤資料與安全控制點。

本篇會學到什麼

  • 辨識 fake parameterization 與 placeholder 限制
  • 審查 ORM escape hatch 與動態查詢 builder
  • 為資料存取層設計難以誤用的介面

必備名詞與心智模型

真正的參數化是 SQL 結構先固定、值後傳。先把外部資料插入字串,再把整串交給 prepare,結構早已被污染。placeholder 只能代表完整 literal,不能代表關鍵字、欄位、表名或任意 IN 清單。

發生情境與生活化比喻

管理頁以 ORM 搜尋使用者並允許自訂排序。開發者使用 prepared statement 處理名稱,卻把 sort 交給 orderByRaw;或用 PDO prepare 包住已插值的 SQL,誤以為 API 名稱足以防禦。

不安全的原始碼

$sql = "SELECT id,email FROM users WHERE email='" . $email . "'";
$stmt = $pdo->prepare($sql); // prepare 時資料已進入 SQL
$stmt->execute();

$query->where('active', 1)->orderByRaw($_GET['sort']);

此片段刻意省略連線、路由與部署設定,只用來辨識資料流,請勿放入任何服務。

逐步看懂資料如何變成查詢

  1. email 先進入 SQL 字串,prepare 無法還原邊界
  2. ORM 的普通 where 可能安全,但 raw API 直接接受結構
  3. 動態表名、欄位名與排序不能當一般值綁定
  4. 共用 helper 若內部串接,所有呼叫端一起受影響
  5. 測試只用正常資料時很難看出問題

修補後原始碼

$stmt = $pdo->prepare('SELECT id,email FROM users WHERE email=:email');
$stmt->execute(['email'=>$email]);

$allowedSorts = ['email'=>'email', 'created'=>'created_at'];
$sortColumn = $allowedSorts[$_GET['sort'] ?? 'created'] ?? 'created_at';
$query->where('active', 1)->orderBy($sortColumn, 'asc');

修補的共同原則是先固定 SQL 結構,再把值獨立綁定;無法作為值綁定的識別字只能從程式內允許清單產生。

如何驗證修補

  • 搜尋所有 raw、literal、expression、query 與 exec API
  • 檢查 SQL 字串建立位置,而非只看執行位置
  • 每個 placeholder 對應一個完整值
  • IN 清單依元素建立參數,不把逗號字串當一個參數
  • 排序、欄位與租戶 schema 使用固定 mapping


閱讀提醒:以下分析的目的,是讓你能在自己的程式、測試資料與授權環境中辨識風險。面對正式系統時,先取得明確授權並保存變更紀錄;不要用錯誤訊息或一次回應就推論資料外洩,也不要把文章中的概念改造成自動化探測。安全工作的完成條件,是能說明根因、修補位置、回歸結果與剩餘風險。

更多應用情境、常見誤判與觀察方法

把原理放回真實開發情境

ORM 的查詢建構器通常安全處理資料值,但 raw query、字串排序、片段 API 與自訂擴充是常見逃生口。 實作時不要只盯著畫面上的輸入框,還要檢查 API 參數、伺服器端預設值、資料轉換與最後呼叫的資料庫介面。最實用的閱讀方式,是把每一段程式標成「外部來源、轉換、控制、資料庫 sink」,確認資料值沒有在途中重新變回 SQL 結構。

常見誤判

看到問號或命名參數不代表一定綁定;需確認驅動設定、執行 API 與參數型別。 安全判斷不能只靠畫面、單次錯誤或某一個防護產品;應同時看原始碼、驅動程式實際執行方式、資料庫權限與回歸測試。若無法證明查詢結構固定,就應把它視為待修的設計風險。

開發與 SOC 可以觀察什麼

對 raw API 建立集中封裝與 Code Owners,CI 搜尋危險呼叫,執行期再以查詢指紋監控。 日誌宜記錄時間、路由、請求關聯 ID、查詢名稱、結果狀態與耗時,敏感值則遮罩或雜湊。偵測規則的用途是縮短發現時間;阻擋之後仍須回到程式碼修補,並搜尋其他共用元件與相同資料流。

審查時的三個追問

  1. 這個值最早由誰控制,經過哪些轉換?
  2. 它在資料庫呼叫中是資料值,還是欄位、排序、運算式等結構?
  3. 若預防失效,最小權限、監控與事件流程能限制並發現多少影響?

開發與維運防禦清單

  • 封裝只接受 typed filter object 的 repository
  • 限制或 lint raw query API
  • 對共用 query helper 寫正常、特殊字元與邊界測試
  • 升級 ORM/driver 時重新核對參數與 emulation 行為

重點整理與下一篇

工具正確使用是第一層。下一篇將把程式碼、資料庫權限、WAF、日誌、監控、部署與事件回應組成完整防禦架構。

參考資料與更新日期

更新日期:2026-07-20

贊(0) 贊助
未經允許不得轉載:波波的寂寞世界 » 用了 PDO、Prepared Statement 或 ORM 為何仍有漏洞

波波的寂寞世界

Facebook聯繫我們

覺得文章有幫助,歡迎贊助支持本站

感謝你的支持,我會持續整理資安筆記、實務經驗與有價值的技術內容。

贊助本站