java sql注入漏洞审计
执行对象
执行对象是sql的执行者。
目前常用的执行对象接口有三种: Statement
、PreparedStatement
和CallableStatement
。
Statement
Statement主要用于执行静态sql语句,即内容不变的sql语句。它每一次执行都需要对传入的sql语句进行编译,效率较低。
例如:
1 | String name = "tom"; |
假如说name为可控的话,当我传入' or 1=1 #
则sql语句就会变为
select * from student_table where student_name ='' or 1=1 #
,可以看到程序输出表中所有数据。
PreparedStatement
PreparedStatement是预编译参数化查询执行SQL语句的方式。
1 | String sql = "select * from student_table where student_name = ?"; |
看到使用PreparedStatement之后,特殊符号被转义,无法按照设想的sql语句执行
CallableStatement
CallableStatement接口提供了执行存储过程的方法。
该方法用的不是很多,不做过多介绍。
Mybatis框架
MyBatis 是⼀款优秀的持久层框架,它⽀持定制化 SQL、存储过程以及⾼级映射。MyBatis 避免 了⼏乎 所有的 JDBC 代码和⼿动设置参数以及获取结果集。MyBatis 可以使⽤简单的 XML 或注 解来配置和映 射原⽣类型、接⼝和 Java 的 POJO(Plain Old Java Objects,普通⽼式 Java 对 象)为数据库中的记 录。
基于xml实现mybatis基本使用
实体类Student各个参数与数据库中目标表的列名一一对应,包括参数名,参数类型。
Dao接口文件:
1 | package com.mybatis.dao; |
MapperXmlSQL语句映射文件:
1 | <?xml version="1.0" encoding="UTF-8" ?> |
MyBatis.xml配置文件:
1 | <?xml version="1.0" encoding="UTF-8"?> |
测试类:
1 | package com.mybatis; |
Mybatis中存在的注入问题
动态sql
动态sql是mybatis的主要特性之一,在mapper中定义的参数传到xml中之后,在查询之前mybatis会对其进行动态解析。mybatis框架中,接受用户参数有两种方式:
- ${param}方式
- #{param}方式
#{}会自动传入值加上单引号,而${}不会。
例如刚才的:
1 | <?xml version="1.0" encoding="UTF-8" ?> |
传入的name为tom
那么实际提交的sql语句就为:select * from student_table where student_name = 'tom'
但是假如说把#改成$,就会发生报错。
因为执行的sql语句为:select * from student_table where student_name = tom
不安全的${}
修改mapper文件,我们拼接引号:
1 | <?xml version="1.0" encoding="UTF-8" ?> |
此时如果提交 ' or 1=1 #
则sql语句就成为了select * from student_table where student_name = '' or 1=1 #
而假如说使用的是#{}的话
则sql语句会处理为select * from student_table where student_name = ?
java中常见的注入场景
like模糊匹配
1 | <?xml version="1.0" encoding="UTF-8" ?> |
mybatis会处理为:select * from student_table where student_name like '%?%'
因为引号内的?会被当做值处理,所以这里会报错找不到占位符。
所以有些开发不得不使用$去代替#符号,用来完成他们想完成的功能。
比如这样:
1 | <?xml version="1.0" encoding="UTF-8" ?> |
但是这样就可能造成sql注入。
In语句
同样对dao文件进行修改
1 | public interface StudentDao { |
Mapper文件:
1 | <?xml version="1.0" encoding="UTF-8" ?> |
提交的id=“1,2”,实际提交的sql语句为:select * from student_table where student_id in ('1,2')
但是这样只能查出一个结果来。
修改mapper文件接受用户输入为$。此时实际提交的SQL语句为:select * from student_table where student_id in (1,2)
提交的id为1) or 1=1 #
此时就可能造成sql注入。 select * from student_table where student_id in (1) or 1=1 #
order by和group by语句
凡是字符串但又不能加引号的位置都不能预编译参数化,这种场景不仅order by还有group by
比如执行sql语句:select * from student_table order by student_age
根据student_age默认按照升序排列。
如果执行sql语句:select * from student_table order by 'student_age'
。数据库表则会原样输出,没有任何排序,因为order by 后跟一个字符串,为常量列排序。而常量列所有值都相等,所以也就不会有任何排序。
同样的,这个体现在程序里面也只能使用$号来接受变量。
但是$号,也会随之带来注入的问题,比如提交sql语句为:if(1=1,student_age,student_tele)
。则实际提交的sql语句为:select * from student_table orer by if(1=1,student_age,student_tele)
同理 group by也与 order by 一样。
总结
容易产生sql注入漏洞的场景有
- 模糊查询
- in查询
- order by ,group by 查询
因为在项目中,直接使用#等预编译处理会报错,所以程序员在开发中不得已使用了拼接,成为了注入的高发点。