@ApiOperation(value = "vul: JDBC语句拼接") @GetMapping("/vul1") public String vul1(String id) { StringBuilderresult=newStringBuilder(); Stringsql="select * from users where id = '" + id + "'";
进一步,使用字符串动态拼接可实现不同关键字的注入,如 like注入、in注入和 order by 注入。
1 2 3 4 5 6 7
Stringsql_like="select * from users where id like '%" + id + "%'"; Stringsql_order="select * from users order by " + column_name; Stringsql_in="delete from users where id in(" + del_ids +");"
// 1. 参数动态拼接 @Select("select * from users where id = ${id}") List<User> queryByIdAsString(@Param("id") String id); // 2. order by关键字注入,使用 #{} 会产生报错,因此容易写成 ${} @Select("select * from users order by ${field} ${sort}") List<User> orderBy2(@Param("field") String field, @Param("sort") String sort); // 3. like关键字注入,搜索时使用 '%#{q}%' 会报错,因此容易写成 ${} @Select("select * from users where user like '%${user}%'") List<User> searchVul(String user); // 4. in关键字注入, 搜索时使用 '%#{q}%' 会报错,因此容易写成 ${} @Select("select * from users where id in (${ids})") List<User> queryIdInIds(@Param("ids") String ids); }
防御思路
对于直接动态拼接,把 ${} 改成 #{}即可;对于 order by、like、in 等关键字,由于报错问题,需要微调写法。
动态拼接安全代码,直接替换即可
1 2 3 4 5
publicinterfaceUserMapper {
@Select("select * from users where id = #{id}") List<User> queryByIdAsString(@Param("id") String id); }
order by 注入安全代码,通过 choose 标签设置 filed 白名单,然后再做对应处理去解决这个问题
title:"UserMapper.xml"
1 2 3 4 5 6 7 8 9 10 11 12 13 14
<selectid="orderBySafe"resultType="com.best.hello.entity.User"> select * from users <choose> <whentest="field == 'id'"> order by id desc </when> <whentest="field == 'user'"> order by user desc </when> <otherwise> order by pass desc limit 2 </otherwise> </choose> </select>
like 注入安全代码,需要使用 concat 做字符串拼接,否则会报错的
title:"UserMapper.java"
1 2 3 4
publicinterfaceUserMapper { @Select("select * from users where user like CONCAT('%', #{user}, '%')") List<User> searchSafe(@Param("user") String user); }
in 注入安全代码,需要使用到 foreach 标签
title:"UserMapper.xml"
1 2 3 4
<selectid="findByUserNameVuln04"parameterType="String"resultMap="User"> select * from users where id in <foreachcollection="ids"item="item"open="("separatosr=","close=")">#{ids} </foreach> </select>
总结
Java SQL 注入漏洞比较有意思的地方是,Java 由于 Mybatis 的存在,对数据库操作进一步封装的同时,却不支持例如 order by 语句的预编译,导致开发者会偏向于使用更为”便捷“的 ${},进而导致漏洞产生。
当然值得庆幸的是,Mybatis Plus 目前已经支持上述语句的预编译,所以及时更新组件也是漏洞防御一大重点;不过也需要考虑项目兼容性和供应链安全的问题。