您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“Mybatis的SQL注入實例分析”,內容詳細,步驟清晰,細節處理妥當,希望這篇“Mybatis的SQL注入實例分析”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
MyBatis3提供了新的基于注解的配置。主要在MapperAnnotationBuilder中,定義了相關的注解:
public MapperAnnotationBuilder(Configuration configuration, Class<?> type) { ... sqlAnnotationTypes.add(Select.class); sqlAnnotationTypes.add(Insert.class); sqlAnnotationTypes.add(Update.class); sqlAnnotationTypes.add(Delete.class); ...... sqlProviderAnnotationTypes.add(SelectProvider.class); sqlProviderAnnotationTypes.add(InsertProvider.class); sqlProviderAnnotationTypes.add(UpdateProvider.class); sqlProviderAnnotationTypes.add(DeleteProvider.class); }
增刪改查占據了絕大部分的業務操作,通過注解不在需要配置繁雜的xml文件,越來越多的sql交互均通過注解來實現。從MapperAnnotationBuilder可以看到Mybatis提供了以下相關的注解:
@Select
@Insert
@Update
@Delete
@SelectProvider
@InsertProvider
@UpdateProvider
@DeleteProvider
例如如下例子,使用@Select注解直接編寫SQL完成數據查詢:
@Mapper public interface UserMapper { @Select("select * from t_user") List<User> list(); }
使用類似@SelectProvider高級注解可以指定某個工具類的方法來動態編寫SQL,以應對復雜的業務需求。
以@SelectProvider 為例,查看具體的實現,主要包含兩個注解屬性,其中type表示工具類,method 表示工具類的某個方法,用于返回具體的SQL:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface InsertProvider { // 用于指定獲取 sql 語句的指定類 Class<?> type(); // 指定類中要執行獲取 sql 語句的方法 String method(); }
使用方法如下,在ProjectSql類的getContentByProjectIds方法定義相關的sql即可,sql的定義可以通過org.apache.ibatis.jdbc.SQL來快速實現:
@SelectProvider(type = ProjectSql.class, method = "getContentByProjectIds")
List<Integer> getContentByProjectIds(List<Integer> projectIds);
實際上跟xml配置中對應的標簽語法是一樣的(例如@Select對應<select>標簽),所以注入場景也是類似的。
在Mybatis中,#的作用主要是替換預編譯語句(PrepareStatement)中的占位符?,$是直接的SQL拼接。以like模糊查詢 為例子:
例如如下例子:
跟xml配置一樣,like模糊查詢直接使用#預編譯的方式進行注解的話會觸發異常,所以很多時候直接使用$進行注解:
@Select("SELECT id, name, age, email FROM user where name like '${name}'")List<User> queryUserByName(@Param("name") String name);
那么此時name前端用戶可控的話,將導致SQL注入風險。
圖片查看sql日志,成功執行1/0觸發sql error,說明注入成功:
處理這類SQL問題也很簡單,使用sql的內置函數進行拼接,拼接后再采用#預編譯的方式進行查詢。例如上面案例是h3數據庫的,使用'||'拼接再進行預編譯處理即可:
@Select("SELECT id, name, age, email FROM user where name like '%'||#{name}||'%'")List<User> queryUserByName(@Param("name") String name);
此時已使用預編譯進行SQL查詢:
此外,類似Order by、動態表名,無法采用預編譯的方式情況,可以在在代碼層使用間接引用的方式進行處理。
對于范圍查詢in,熟悉mybatis注入的話,是需要使用MyBatis自帶的循環指令foreach來解決SQL語句動態拼接的,當使用注解時,就需要使用< script>標簽來引入foreach了。
要在帶普通注解的映射器接口類中使用動態 SQL,可以使用script 元素。跟xml類似,主要是如下的元素:
if choose (when, otherwise) trim (where, set) foreach
相關的注入場景跟2.1也是類似的。也是離不開$。此外,在進行同條件多值查詢(例如范圍查詢in)的時候,可以使用MyBatis自帶的循環指令foreach來解決SQL語句動態拼接的問題。
可以通過使用Provider注解指定某個工具類的方法來動態編寫SQL。以@SelectProvider為例:
首先在mapper中使用@SelectProvider定義相關的方法,其中type表示工具類,method 表示工具類的某個方法,用于返回具體的SQL。例如下面的例子:
通過傳遞userIds以及name,查詢相關的用戶信息,在UserInfoSql類的getUserInfoByids方法定義了具體的SQL內容:
/** * @param userIds 必填 * @param name 可選 * @return */ @SelectProvider(type = UserInfoSql.class, method = "getUserInfoByids") List<User> getUserInfoByids(List<Long> userIds, String name); class UserInfoSql { public String getUserInfoByids(List<Long> userIds, String name) { SQL sql = new SQL(); sql.SELECT("id, name, age, email"); sql.FROM("user"); sql.WHERE("id in(" + Joiner.on(',').join(userIds) + ")"); if(StringUtil.isNotBlank(name)){ sql.WHERE("name like '%" + name + "%'"); } sql.ORDER_BY("id desc"); return sql.toString(); } }
在Controller調用具體方法就可以進行sql查詢了:
@RequestMapping(value = "/getUserInfoByids") public List<User> getUserInfoByids(String name,@RequestParam List<Long> userIds){ List<User> userList = userMapper.getUserInfoByids(userIds,name); return userList; }
正常請求返回對應的用戶信息:
前面是通過MyBatis 3 提供的工具類org.apache.ibatis.jdbc.SQL來生成SQL的。該類提供了類似select、where、ORDER_BY等方法來完成SQL生成的操作。這里有個誤區,很多開發認為這里工具類會進行相關的預編譯處理。
實際上Provider其實只需要返回一個SQL字符串,工具類只不過用了一些關鍵字做格式化而已,甚至可以直接使用StringBuffer拼接SQL語句。同樣是上面的例子,List userIds是long類型,但是name是String類型,可以嘗試注入:
查看相關日志,成功執行1/0邏輯觸發SQL error,也印證了Provider實際上只是 SQL 拼接,沒有做相關的安全處理 :
相比@Select@,SelectProvider 只是在定義注解的方式上有所不同, 前者是直接定義 sql, 一個是在外部定義好 sql 直接引用, 沒本質上的區別,所以解決方法是在對應的sql場景,使用#進行預編譯進行處理,例如這里的like模糊查詢和in范圍查詢:
@SelectProvider(type = UserInfoSql.class, method = "getUserInfoByids") List<User> getUserInfoByids(@Param("userIds")List<Long> userIds,@Param("name")String name); class UserInfoSql { public String getUserInfoByids(@Param("userIds")List<Long> userIds, @Param("name")String name) { StringBuilder sql = new StringBuilder(128); sql.append("< script>SELECT id, name, age, email FROM user WHERE (id in"); sql.append("<foreach item='item' collection='userIds' open='(' separator=',' close=')'>#{item}</foreach>"); if(StringUtil.isNotBlank(name)){ sql.append("and name like '%'||#{name}||'%')"); } sql.append("ORDER BY id desc</script>"); return sql.toString(); } }
查看sql日志,此時使用預編譯進行sql處理,避免了SQL注入風險。
讀到這里,這篇“Mybatis的SQL注入實例分析”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。