首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

iBatis/MyBatis SQL 注入防護:#{} 與 ${} 關鍵差異與最佳實務

     編輯:關於SqlServer

核心結論

  • #{property} (Parameter Placeholder) → **預編譯參數化查詢** → **防止 SQL 注入**。
  • ${property} (String Substitution) → **字串直接替換** → **極高風險,嚴禁用於非受信任輸入**。

實例對比

安全寫法:使用 #{} (預編譯)

<select id="findByName" resultType="User">
    SELECT * FROM t_user WHERE name LIKE '%' || #{name} || '%'
</select>

生成 SQL:SELECT * FROM t_user WHERE name LIKE '%' || ? || '%'
JDBC 驅動將使用者輸入視為參數值,單引號等特殊字元自動轉義,徹底阻斷注入。

危險寫法:使用 ${} (字串替換)

<select id="findByNameUnsafe" resultType="User">
    SELECT * FROM t_user WHERE name LIKE '%' || ${name} || '%'
</select>

若使用者輸入 `test' OR '1'='1`,生成 SQL 變為:
SELECT * FROM t_user WHERE name LIKE '%' || 'test' OR '1'='1' || '%'
邏輯被竄改,返回所有使用者。

為何 #{} 能防注入?

  1. SQL 發送給資料庫前已完成解析、編譯、優化,形成執行計畫。
  2. 參數佔位符 (?) 僅在執行階段綁定值,資料庫將其視為**資料**而非**程式碼**。
  3. 單引號、註解符號 (--, /*) 等特殊字元失去語法意義,僅作為字面字元處理。

${} 合法使用場景 (極少數)

  • 動態指定 **資料表名稱、欄位名稱、排序欄位** 等無法參數化的識別符:
    ORDER BY ${sortColumn} ${sortOrder}
  • 前提:輸入值**必須經過嚴格白名單驗證** (如僅允許 `id, name, created_at`),絕不可直接透傳前端參數。

MyBatis 進階防護建議

  1. 全專案掃描:CI/CD 管線加入靜態分析規則 (如 SpotBugs FindSecBugs、SonarQube) 偵測 `${}` 用於 WHERE/VALUES/SET 子句。
  2. 代碼審查清單:每個 `${}` 必須附註說明「已驗證白名單」且有單元測試覆蓋。
  3. 動態 SQL 優先用 #{} + <if><choose><where><trim> 構建條件,避免字串拼接。
  4. 資料庫層面:應用帳號僅授予必要權限 (SELECT/INSERT/UPDATE/DELETE),禁止 DDL、EXECUTE 等高權限。

常見誤區澄清

誤區事實
「Oracle 不用擔心注入」所有支援參數化查詢的資料庫皆需使用參數化;Oracle 同樣易受注入攻擊。
「#{ } 處理 LIKE 通配符麻煩」在 Java 端組裝:`param = "%" + userInput + "%"`,再傳入 `#{param}`,兼具安全與便利。
「存儲過程呼叫不需參數化」`{call proc(#{param})}` 同樣需用 `#{}`,避免存儲過程內部動態 SQL 注入。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved