在使用MyBatis逆向工程生成的Example查詢模式時,很多開發者看到XML中存在${}
占位符就會擔心SQL注入問題。但實際上,存在${}
并不等同于存在SQL注入風險。本文將詳細分析何時會存在真正的注入風險。
存在SQL注入的兩個關鍵前提
前提一:Criteria存在自定義擴展且接受外部輸入
MyBatis Generator生成的標準Criteria類是相對安全的,但如果開發者添加了自定義擴展方法,就可能引入風險:
// 危險的自定義擴展示例
public class MTaxSbPayExample {public static class Criteria extends GeneratedCriteria {// ? 危險:允許用戶直接傳入SQL條件public Criteria andCustomCondition(String sqlCondition, Object value) {addCriterion(sqlCondition, value, "custom");return (Criteria) this;}// ? 危險:動態表名或列名public Criteria andDynamicColumn(String columnName, String operator, Object value) {addCriterion(columnName + " " + operator, value, columnName);return (Criteria) this;}}
}
如果用戶輸入直接傳遞給這些方法:
// 惡意輸入示例
String userInput = "1=1; DROP TABLE users;--";
example.createCriteria().andCustomCondition(userInput, "someValue");
前提二:Example對象作為接口參數且orderByClause被不當賦值
另一個風險點是orderByClause
字段,因為ORDER BY子句通常需要使用${}
來處理列名:
<if test="orderByClause != null">order by ${orderByClause}
</if>
危險場景:
// ? 危險:直接將用戶輸入作為排序條件
@RestController
public class DataController {public List<MTaxSbPay> getData(@RequestParam String sortBy) {MTaxSbPayExample example = new MTaxSbPayExample();// 直接使用用戶輸入,存在注入風險example.setOrderByClause(sortBy); return mapper.selectByExample(example);}
}
惡意請求:
GET /getData?sortBy=id; DROP TABLE users;--
為什么存在${}
不一定有SQL注入風險
1. 硬編碼的安全使用
在標準的MyBatis Generator實現中,${criterion.condition}
中的condition是硬編碼的:
// 生成的安全方法
public Criteria andIdEqualTo(String value) {addCriterion("ID =", value, "id"); // "ID =" 是硬編碼字符串return (Criteria) this;
}
對應的XML處理:
<when test="criterion.singleValue">and ${criterion.condition} #{criterion.value}
</when>
最終生成安全的SQL:
WHERE ID = ? -- 參數: 用戶輸入值
2. 參數分離的設計模式
MyBatis Generator采用了條件與參數分離的設計:
- 條件部分(
${criterion.condition}
):硬編碼的操作符,如=
、>
、LIKE
- 參數部分(
#{criterion.value}
):用戶輸入,通過參數化查詢處理
這種設計確保了即使使用${}
,也不會直接拼接用戶輸入的內容。
3. 用戶輸入路徑受限
標準的Criteria類只提供預定義的方法:
// 用戶只能通過這些安全的方法構建查詢
criteria.andIdEqualTo(userInput); // 安全
criteria.andNameLike("%" + userInput + "%"); // 安全
criteria.andStatusIn(Arrays.asList("ACTIVE", "INACTIVE")); // 安全
用戶無法直接控制criterion.condition
的值,只能影響criterion.value
,而后者是參數化處理的。
總結
MyBatis的${}
占位符本身并不等同于SQL注入漏洞。關鍵在于:
- 數據來源:是硬編碼還是用戶輸入?
- 使用方式:是否進行了適當的驗證和過濾?
- 設計模式:是否采用了參數分離的安全設計?
標準的MyBatis Generator生成的Example代碼通常是安全的,真正的風險往往來自于開發者的不當擴展和使用。