JAVA常用框架SQL注入审计
字数 1606 2025-08-26 22:11:28
Java常用框架SQL注入审计指南
一、JDBC拼接不当造成的SQL注入
1. Statement与PreparedStatement的区别
JDBC执行SQL语句有两种方式:
- Statement:直接拼接SQL语句,存在SQL注入漏洞
- PreparedStatement:对SQL语句进行预编译,能有效防止SQL注入
2. Statement示例(存在漏洞)
@RequestMapping("/jdbc/vuln")
public String jdbc_sqli_vul(@RequestParam("username") String username) {
Statement statement = con.createStatement();
String sql = "select * from users where username = '" + username + "'";
ResultSet rs = statement.executeQuery(sql);
// ...
}
攻击示例:username = joychoun' or '1' = '1'
3. PreparedStatement示例(安全)
@RequestMapping("/jdbc/sec")
public String jdbc_sqli_sec(@RequestParam("username") String username) {
String sql = "select * from users where username = ?";
PreparedStatement st = con.prepareStatement(sql);
st.setString(1, username);
ResultSet rs = st.executeQuery();
// ...
}
4. JDBC易产生漏洞点
4.1 未使用占位符
直接拼接SQL语句仍会存在SQL注入漏洞
4.2 使用IN语句
String sql = "delete from users where id in(" + delIds + "); //存在SQL注入
解决方案:遍历传入的对象个数,使用"?"占位符
4.3 使用LIKE语句
String sql = "select * from users where password like '%" + con + "%'"; //存在SQL注入
4.4 %和_未过滤
预编译不能处理这些符号,需要手动过滤,否则会造成慢查询(DOS)
4.5 ORDER BY等无法预编译
String sql = "Select * from news where title =? " + "order by '" + time + "' asc";
解决方案:需要手动过滤
二、MyBatis框架下的SQL注入
1. ${}与#{}的区别
${Parameter}:直接拼接SQL,存在注入风险#{Parameter}:预编译方式,安全
2. ${}示例(存在漏洞)
@Select("select * from users where username = '${username}'")
List<User> findByUserNameVuln01(@Param("username") String username);
3. #{}示例(安全)
@Select("select * from users where username = #{username}")
User findByUserName(@Param("username") String username);
4. MyBatis易产生SQL注入的场景
4.1 模糊查询
错误写法:
<select id="findByUserNameVuln02" parameterType="String" resultMap="User">
select * from users where username like '%${_parameter}%'
</select>
正确写法:
<select id="findByUserNamesec" parameterType="String" resultMap="User">
select * from users where username like concat('%',#{_parameter},'%')
</select>
不同数据库的正确写法:
- MySQL:
concat('%',#{username},'%') - Oracle:
'%' || #{username} || '%' - SQLServer:
'%' + #{username} + '%'
4.2 使用IN语句
错误写法:
<select id="findByUserNameVuln04" parameterType="String" resultMap="User">
select * from users where id in (${id})
</select>
正确写法:使用foreach
id in <foreach collection="ids" item="item" open="(" separator="," close=")">
#{ids}
</foreach>
4.3 使用ORDER BY语句
与JDBC同理,需要手动过滤
三、MyBatis-Plus框架下的SQL注入
1. 条件构造器Wrapper
Wrapper:条件构造抽象类AbstractWrapper:查询条件封装LambdaQueryWrapper:Lambda语法查询QueryWrapper:Entity对象封装操作
2. 常见注入场景
2.1 apply方法
错误写法(存在漏洞):
wrapper.eq("author", author).apply("title=" + title);
正确写法:
wrapper.eq("author", author).apply("title={0}", title);
2.2 last方法
wrapper.last("order by " + column); //存在SQL注入风险
2.3 exists/notExists
wrapper.exists("select title from tutorials where title = " + title); //存在风险
2.4 having
wrapper.select().groupBy("author").having("id > " + id); //存在风险
2.5 orderBy
wrapper.select().orderBy(true, true, column); //存在风险
2.6 groupBy
wrapper.select().groupBy(column); //存在风险
2.7 inSql/notInSql
wrapper.select().inSql(column, "select id from tutorials where id >" + id); //存在风险
3. 分页插件风险
3.1 PaginationInnerInterceptor
personPage.addOrder(OrderItem.asc(order)); //order未过滤则存在SQL注入
3.2 pagehelper风险方法
com.github.pagehelper.Page.setOrderBy(String)com.github.pagehelper.page.PageMethod.startPage(int,int,String)com.github.pagehelper.PageHelper.startPage(int,int,String)
四、Hibernate框架下的SQL注入
1. HQL注入
错误写法:
String hql = "from People where username = '" + username + "' and password = '" + password + "'";
2. 安全参数绑定方式
2.1 命名参数
Query<User> query = session.createQuery("from users name = :name", User.class);
query.setParameter("name", parameter);
2.2 位置参数
Query<User> query = session.createQuery("from users name = ?1", User.class);
query.setParameter(1, parameter);
2.3 命名参数列表
Query<User> query = session.createQuery("from users where name in (:names)", User.class);
query.setParameter("names", names);
2.4 类实例
user1.setName("g1ts");
Query<User> query = session.createQuery("from users where name =:name", User.class);
query.setProperties(user1);
2.5 HQL拼接过滤
String str = StringEscapeUtils.escapeSql("'");
3. 原生SQL风险
错误写法:
Query<People> query = session.createNativeQuery("select * from user where username = '" + username + "' and password = '" + password + "'");
正确写法:
Query<User> query = session.createNativeQuery("select * from user where name = :name");
query.setParameter("name", parameter);
总结
- 始终优先使用预编译:在JDBC中使用PreparedStatement,在MyBatis中使用#{}
- 特殊场景需注意:ORDER BY、GROUP BY、表名等无法预编译的场景需要手动过滤
- 框架特定风险:了解各ORM框架的特殊风险点(如MyBatis的${},Hibernate的HQL拼接)
- 过滤特殊字符:对于必须拼接的场景,使用StringEscapeUtils等工具过滤
- 审计重点:关注SQL语句拼接点、动态SQL生成点、ORM框架的特殊方法