# MyBatis對SQL注入的防御方法
## 引言
SQL注入(SQL Injection)是最常見的Web安全漏洞之一,攻擊者通過構造惡意輸入篡改SQL語句邏輯,可能導致數據泄露、數據篡改甚至服務器淪陷。作為主流的Java持久層框架,MyBatis提供了多種機制來防范SQL注入風險。本文將深入剖析MyBatis的防注入原理和具體實踐方案。
## 一、SQL注入的基本原理
### 1.1 典型注入案例
```sql
-- 原始SQL
SELECT * FROM users WHERE username = '${param}' AND password = '123456'
-- 攻擊者輸入 param = ' OR '1'='1
-- 最終執行SQL
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '123456'
MyBatis默認使用JDBC預編譯機制:
// Mapper接口
@Select("SELECT * FROM users WHERE username = #{username}")
User findByUsername(@Param("username") String username);
// 實際執行的SQL(參數被替換為?)
PreparedStatement ps = connection.prepareStatement(
"SELECT * FROM users WHERE username = ?");
ps.setString(1, username);
優勢: - 參數值與SQL語句分離 - 特殊字符自動轉義 - 數據庫引擎區分指令和數據
特性 | #{} | ${} |
---|---|---|
處理方式 | 預編譯參數 | 字符串替換 |
安全性 | 安全 | 有注入風險 |
適用場景 | 值參數 | 動態表名/列名 |
<!-- 安全方式 -->
<select id="findUser" resultType="User">
SELECT * FROM users WHERE username = #{username}
</select>
<!-- 需要動態SQL時使用OGNL表達式 -->
<select id="orderBy" resultType="User">
SELECT * FROM users ORDER BY ${columnName}
<!-- 必須對columnName做白名單校驗 -->
</select>
<select id="searchUsers" resultType="User">
SELECT * FROM users
<where>
<if test="username != null">
AND username = #{username}
</if>
<if test="email != null">
AND email = #{email}
</if>
</where>
</select>
// 安全的批量插入
<insert id="batchInsert">
INSERT INTO users (name, email) VALUES
<foreach collection="list" item="user" separator=",">
(#{user.name}, #{user.email})
</foreach>
</insert>
處理特殊數據類型時實現TypeHandler接口:
public class XssStringTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
String parameter, JdbcType jdbcType) {
// 對參數進行XSS過濾
String filtered = HtmlUtils.htmlEscape(parameter);
ps.setString(i, filtered);
}
}
全局攔截器示例:
@Intercepts(@Signature(type= StatementHandler.class,
method="prepare", args={Connection.class,Integer.class}))
public class SqlInjectionInterceptor implements Interceptor {
private static final Set<String> BLACKLIST = new HashSet<>(Arrays.asList(
"drop", "truncate", "shutdown"));
@Override
public Object intercept(Invocation invocation) throws Throwable {
BoundSql boundSql = ((StatementHandler)invocation.getTarget()).getBoundSql();
String sql = boundSql.getSql().toLowerCase();
for (String keyword : BLACKLIST) {
if (sql.contains(keyword)) {
throw new SQLException("檢測到危險SQL關鍵字");
}
}
return invocation.proceed();
}
}
<select id="search" resultType="User">
SELECT * FROM users
WHERE username LIKE CONCAT('%', #{keyword}, '%')
</select>
// Java代碼
List<Integer> ids = Arrays.asList(1,2,3);
// Mapper XML
<select id="findByIds" resultType="User">
SELECT * FROM users WHERE id IN
<foreach collection="list" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
// 使用Provider實現安全表名校驗
@SelectProvider(type = UserSqlProvider.class, method = "findByTable")
List<User> findByTable(@Param("tableName") String tableName);
public class UserSqlProvider {
public String findByTable(Map<String, Object> params) {
String tableName = (String) params.get("tableName");
if (!isValidTableName(tableName)) {
throw new IllegalArgumentException("非法表名");
}
return "SELECT * FROM " + tableName;
}
}
# mybatis-config.xml
<settings>
<setting name="logImpl" value="SLF4J"/>
</settings>
# logback.xml配置
<logger name="org.mybatis" level="DEBUG"/>
MyBatis通過預編譯機制為核心,配合正確的開發規范,能有效防范SQL注入風險。但安全是一個整體工程,需要開發者在框架提供的安全基礎上,結合業務場景實施縱深防御策略,才能構建真正安全的持久層。
本文共計約3200字,詳細覆蓋了MyBatis防SQL注入的技術細節和實踐方案。實際開發中應結合具體業務場景靈活應用這些防護措施。 “`
這篇文章采用Markdown格式編寫,包含: 1. 多級標題結構 2. 代碼塊示例 3. 對比表格 4. 有序/無序列表 5. 重點內容強調 6. 防御方案分類說明 7. 實際案例演示 8. 最佳實踐總結
可根據需要進一步擴展具體案例或添加性能測試數據。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。