一方面数据映射框架如mybatis、hiberate等在框架底层已经实现了对sql注入的防御,另一方面白盒/黑盒等扫描器也能解决一部分sql注入问题,sql注入的出现风险被大大降低。但是在研发人员未能恰当的使用框架或者sql语句拼接不当的的情况下,仍然可能导致sql注入的风险。本文分析sql注入的几种情况以及RASP在其中的作用。
1.JDBC 拼接不当造成的SQL注入
JDBC有两种方法执行SQL语句,分别为PrepareStatement和Statement。两个方法的区别在于PrepareStatement会对SQL语句进行预编译,而Statement方法在每次执行时都需要编译,会增大系统开销。理论上PrepareStatement的效率和安全性会比Statement要好,但并不意味着使用PrepareStatement就绝对安全,不会产生SQL注入。
PrepareStatement方法支持使用‘?’对变量位进行占位,在预编译阶段填入相应的值构造出完整的SQL语句,此时可以避免SQL注入的产生。但开发者有时为了便利,会直接采取拼接的方式构造SQL语句,此时进行预编译则无法阻止SQL注入的产生。如以下代码所示,PrepareStatement虽然进行了预编译,但在以拼接方式构造SQL语句的情况下仍然会产生SQL注入。代码示例如下(若使用“or 1=1”,仍可判断出这段代码存在SQL注入)
String sql = "select * from user where id =" + req.getParameter("id");
out.println(sql);
try{
PreparedStatement pstt = con.prepareStatement(sql);
ResultSet re = pstt.executeQuery();
while(rs.next()){
out.println("<br>id:"+rs.getObject("id"));
out.println("<br>name:"+re.getObject("name"));
}
catch(SQLException e){
e.printStackTrace();
}
}
正确地使用PrepareStatement可以有效避免SQL注入的产生,使用“?”作为占位符时,填入对应字段的值会进行严格的类型检查。将前面的“拼接构造SQL语句”改为如下“使用占位符构造SQL语句”的代码片段,即可有效避免SQL注入的产生。
PrintWriter out = resp.getWriter();
String sql = "select * from user where id = ?"
out.println(sql);
try{
PreparedStatement pstt = con.prepareStatement(sql);
pstt.setInt(1,Integer.parseInt(req.getParameter("id")));
ResultSet rs = pstt.executeQuery();
// sql执行结果的代码省去....
}
2.框架使用不当造成SQL注入
如今的Java项目或多或少会使用对JDBC进行更抽象封装的持久化框架,如MyBatis和Hibernate。通常,框架底层已经实现了对SQL注入的防御,但在研发人员未能恰当使用框架的情况下,仍然可能存在SQL注入的风险。
Mybatis框架
MyBatis框架的思想是将SQL语句编入配置文件中,避免SQL语句在Java程序中大量出现,方便后续对SQL语句的修改与配置。MyBatis中使用parameterType向SQL语句传参,在SQL引用传参可以使用#{Parameter}和${Parameter}两种方式
使用#{Parameter}构造SQL的代码如下所示
<select id="getUsername" resultType="com.example.bean.User">
select id,name,age from user where name #{name}
<select>
从Debug回显的SQL语句执行过程可以看出,使用#{Parameter}方式会使用“?”占位进行预编译,因此不存在SQL注入的问题。用户可以尝试构造“name”值为“z1ng or 1=1”进行验证。回显如下,由于程序未查询到结果出现了空指针异常,因此此时不存在SQL注入。
使用${Parameter}构造SQL的代码如下所示:
<select id = "getUsername" resultType = "com.example.bean.User">
select id,name,age from user where name = ${name}
<select>
name”值被拼接进SQL语句之中,因此此时存在SQL注入。${Parameter}采用拼接的方式构造SQL,在对用户输入过滤不严格的前提下,此处很可能存在SQL注入。
Hibernate
Hibernate是一种ORM框架,全称为 Object_Relative DateBase-Mapping,Hibernate框架是Java持久化API(JPA)规范的一种实现方式。Hibernate 将Java 类映射到数据库表中,从 Java 数据类型映射到 SQL 数据类型。Hibernate是目前主流的Java数据库持久化框架,采用Hibernate查询语言(HQL)注入
HQL的语法与SQL类似,受语法的影响,HQL注入在实际漏洞利用上具有一定的限制。
3.jrasp mysql检测模块
不同sql client版本,hook类差别大,这里以mysql8.x 为例子说明。
1)可配置参数:
2)sql拼接hook
com.mysql.cj.jdbc.StatementImpl
3)sql 预编译hook
com.mysql.cj.jdbc.ClientPreparedStatement,com.mysql.cj.jdbc.PreparedStatement
4)检测算法
4.sql注入实战
存在sql拼接的业务代码如下
正常sql(返回用户自己的数据)
sql注入(返回了全部用户数据)
5.检测与防御
值得注意的是不同于复杂的正则规则检测, jrasp内置了 sql 注入词法分析防火墙,单条sql检测时间在0.1ms内完成,误报/漏报率极低。
---------------------------------------------------------------------------
🔥🔥🔥国内技术领先的开源RASP社区 https://www.jrasp.com