MyBatis 防止 SQL 注入的正確用法
一句話總結(jié)
MyBatis 防 SQL 注入的核心是:參數(shù)一律使用 #{} 做“參數(shù)綁定”(PreparedStatement 的 ? 占位),讓“SQL 結(jié)構(gòu)”和“數(shù)據(jù)”徹底分離;對不得不用 ${} 的 表名/列名/排序 等 SQL 片段,必須做 白名單 或用 <choose> 枚舉替代;同時配合代碼規(guī)范、審計與測試實現(xiàn)多層防護。
1. 先搞清楚:SQL 注入的本質(zhì)是什么?
SQL 注入并不是“沒轉(zhuǎn)義”,而是:把用戶輸入拼進 SQL 語法結(jié)構(gòu)里,導致輸入從“數(shù)據(jù)”變成“指令”。
因此最可靠的防護手段是:
- 參數(shù)綁定(PreparedStatement):輸入永遠作為參數(shù)值傳遞,不參與 SQL 語法解析。
2. MyBatis 兩種替換方式:#{}vs${}(核心)
| 寫法 | 本質(zhì) | 是否安全 | 適用場景 |
|---|---|---|---|
#{param} | 生成 ? 占位符 + JDBC 參數(shù)綁定(TypeHandler) | 安全 | WHERE/INSERT/UPDATE 的“值” |
${param} | 文本原樣拼接到 SQL(字符串替換) | 高風險 | 表名/列名/ORDER BY 等“SQL 片段” |
結(jié)論:
- 能用
#{}就絕不用${}。 ${}只在“無法用?占位”的場景出現(xiàn),并且必須白名單。
3. 正確用法 1:所有“值”都用#{}(參數(shù)綁定)
3.1 基本示例
<select id="findByName" resultType="User">
SELECT * FROM user WHERE name = #{name}
</select>SQL 形態(tài):
SELECT * FROM user WHERE name = ?
此時即使輸入是 admin' OR '1'='1,也只會作為一個字符串值去匹配,不會改變 SQL 結(jié)構(gòu)。
3.2 關(guān)鍵點補充:TypeHandler / JDBC 類型處理
#{} 會走 MyBatis 的 TypeHandler,由它決定用 setString/setInt/... 綁定參數(shù)。
- 這不是簡單“字符串轉(zhuǎn)義”,而是參數(shù)綁定。
- 防注入的關(guān)鍵在于“結(jié)構(gòu)與數(shù)據(jù)分離”。
4. 正確用法 2:動態(tài) SQL 標簽是安全的(前提是內(nèi)部仍用#{})
MyBatis 的動態(tài) SQL(<if> / <where> / <trim> / <choose>)只是動態(tài)組裝 SQL 結(jié)構(gòu),并不等于把用戶輸入拼進 SQL。
只要條件值仍用 #{},就是安全的:
<select id="query" resultType="User">
SELECT * FROM user
<where>
<if test="name != null and name != ''">
AND name = #{name}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>5. 常見攻擊面與正確寫法
5.1LIKE模糊查詢
推薦(仍用 #{}):
WHERE name LIKE CONCAT('%', #{name}, '%')不要用:
WHERE name LIKE '%${name}%'注入后果示例:
若用戶輸入 name 為 ' OR '1'='1,生成的 SQL 將變?yōu)椋?br />WHERE name LIKE '%' OR '1'='1'%'
由于 '1'='1' 恒成立,該查詢會繞過名稱過濾,直接返回全表數(shù)據(jù),導致敏感信息泄露。
5.2IN查詢(批量參數(shù))
使用 <foreach> 生成多個 #{}:
<select id="selectByIds" resultType="User">
SELECT * FROM user
WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>不要把整段 ids 字符串塞進 ${}。
風險/后果:
如果把用戶輸入的 ids 直接拼成 IN (${ids}),攻擊者可構(gòu)造如 1) OR 1=1 --,最終 SQL 可能變?yōu)椋?br />WHERE id IN (1) OR 1=1 --)
從而導致 IN 條件被繞過、返回全表數(shù)據(jù),甚至在支持多語句的配置下進一步造成更嚴重的破壞。
5.3 分頁參數(shù)
分頁參數(shù)也屬于“值”,用 #{}:
SELECT * FROM user LIMIT #{offset}, #{pageSize}6. 不得不用${}的場景:必須白名單/枚舉替代
6.1 為什么這些場景不能用#{}?
JDBC 的 ? 占位符只能替換值,不能替換:
- 表名、列名
ORDER BY的字段ASC/DESC關(guān)鍵字
因此這些場景可能出現(xiàn) ${}。
6.2 推薦做法:用<choose>枚舉排序字段(避免透傳${sortField})
<select id="findUsers" resultType="User">
SELECT * FROM user
<choose>
<when test="sortField == 'age'">ORDER BY age</when>
<when test="sortField == 'name'">ORDER BY name</when>
<otherwise>ORDER BY id</otherwise>
</choose>
<choose>
<when test="sortOrder == 'DESC'">DESC</when>
<otherwise>ASC</otherwise>
</choose>
</select>6.3 業(yè)務(wù)側(cè)白名單映射(如果必須用${})
- 輸入只允許來自固定集合(枚舉/Map 映射),嚴禁直接使用用戶原始字符串。
- 對排序字段、表名、列名做正向校驗(white list),不要做“黑名單過濾”。
7. 常見誤區(qū)(糾錯點)
- 誤區(qū):
#{}是“自動轉(zhuǎn)義”所以安全- 更準確:
#{}是 參數(shù)綁定(結(jié)構(gòu)與數(shù)據(jù)分離),不是靠轉(zhuǎn)義。
- 更準確:
- 誤區(qū):動態(tài) SQL 一定安全
- 動態(tài)標簽本身安全,但如果你在標簽里用
${}拼接用戶輸入,一樣會注入。
- 動態(tài)標簽本身安全,但如果你在標簽里用
- 誤區(qū):把
'${name}'包上引號就安全- 仍是拼接,依然可能被構(gòu)造輸入突破。
8. 多層防護(生產(chǎn)建議)
僅靠 ORM 習慣不夠,建議組合:
- 編碼規(guī)范:禁止
${}透傳用戶輸入;代碼評審強制檢查 - 參數(shù)校驗:對排序字段、表名等做枚舉/白名單校驗
- 審計與測試:單測/集成測試加入注入 payload(如
"' OR '1'='1") - 最小權(quán)限:DB 賬號權(quán)限最小化(禁止高危權(quán)限)
- 可選:WAF/SQL 防火墻/數(shù)據(jù)庫審計(兜底)
9. 面試速答
- Q:MyBatis 如何防 SQL 注入?
- A:對值使用
#{}走 PreparedStatement 參數(shù)綁定;對必須拼接的 SQL 片段(表名/列名/排序)使用白名單或<choose>枚舉,禁止${}透傳用戶輸入。
- A:對值使用
- Q:
${}為什么危險?- A:它是文本替換,輸入會進入 SQL 結(jié)構(gòu),可能變成指令。
- Q:
IN/LIKE怎么寫才安全? - A:
IN用<foreach>+#{};LIKE用CONCAT('%', #{x}, '%')。
到此這篇關(guān)于MyBatis 防止 SQL 注入的正確用法的文章就介紹到這了,更多相關(guān)MyBatis 防止 SQL 注入內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot集成XXL-JOB實現(xiàn)靈活控制的分片處理方案
因為需要并行處理同一張數(shù)據(jù)表里的數(shù)據(jù),所以比較自然地想到了分片查詢數(shù)據(jù),可以利用對 id 取模的方法進行分片,避免同一條數(shù)據(jù)被重復處理,所以本文給大家介紹了SpringBoot集成XXL-JOB實現(xiàn)靈活控制的分片處理方案,需要的朋友可以參考下2024-09-09
IDEA中創(chuàng)建maven項目webapp目錄無法識別即未被標識的解決辦法
在學習SpringMVC課程中,基于IDEA新建maven項目模塊后,webapp目錄未被標識,即沒有小藍點的圖標顯示,所以本文給大家介紹了IDEA中創(chuàng)建maven項目webapp目錄無法識別即未被標識的解決辦法,需要的朋友可以參考下2024-03-03
在Mac系統(tǒng)上安裝Java和IntelliJ?IDEA完整筆記
IntelliJ IDEA的智能代碼輔助功能堪稱一絕,它具備強大的代碼自動補全能力,能依據(jù)上下文精準推測開發(fā)者的意圖,提供極為準確的代碼建議,這篇文章主要介紹了在Mac系統(tǒng)上安裝Java和IntelliJ?IDEA的相關(guān)資料,需要的朋友可以參考下2026-02-02
Jmeter命令行執(zhí)行腳本如何設(shè)置動態(tài)參數(shù)
這篇文章主要介紹了Jmeter命令行執(zhí)行腳本如何設(shè)置動態(tài)參數(shù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-08-08
Spring?Cache?集成?Caffeine實現(xiàn)項目緩存的示例
本文主要介紹了Spring?Cache?集成?Caffeine實現(xiàn)項目緩存的示例,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-12-12

