0x00 前言
记录一下学习java审计的过程。
0x01 JDBC方式
1. 三个对象:
-
connection
connection对象代表数据库
可以设置数据库自动提交。事务提交(connection.commit()),事务回滚(connection.rollback())。
-
statement
调用connnection.createStatement()方法会返回一个statement对象。
是具体执行sql语句
-
PreparedStatement
与statement对象的区别是,不直接放入sql语句,先用?作为占位符进行预编译,等预编译完成后,对?进行赋值,之后调用execute等方法不需要添加参数即可完成执行SQL语句。
所以我们在使用JDBC的时候,使用statement直接拼接SQL语句可能会造成SQL注入。
2. 举例
request.getParameter("userId")
private String getNameByUserId(String userId) {
Connection conn = getConn();//获得连接
String sql = "select name from user where id=" + userId;
Statement stmt = conn.createStatement();
ResultSet rs=stmt.executeUpdate(sql);
}
上面的代码没有经过预编译,然后直接拼接前段获取的参数到sql语句后执行,会产生sql注入;
这边还有另外一个例子:
request.getParameter("userId")
private String getNameByUserId(String userId) {
Connection conn = getConn();//获得连接
String sql = "select name from user where id=" + userId;
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs=pstmt.executeUpdate();
}
这样虽然使用了PreparedStatement,但是没有规范的使用预编译,同样是拼接的方式也会造成sql注入问题;
下面是规范的采用预编译的方式:
//安全的,预编译的,防止了sql注入
Connection conn = getConn();//获得连接
String sql = "select id, username, password, role from user where id=?"; //执行sql前会预编译号该条语句
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, id);
ResultSet rs=pstmt.executeUpdate();
0x02 Mybatis
Mybatis回顾
-
mybatis的maven配置
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency>
-
config.xml 配置数据库连接的文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="dev"> <environment id="dev"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatistest"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="UserMapper.xml"/> </mappers> </configuration>
主要注意配置文件中注册的xml,获取值的方式有两种,分别是
${}
和#{}
。区别:
"#{}"将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号,属于正确的预编译,输入的参数会全部变成查询的部分;
"${}"将传入的数据直接拼接在sql中,造成sql注入。如where username={username},如果传入的值是111,那么解析成sql时的值为where username=111;如果传入的值是1 and 1=1 ;,则解析成的sql为:select id, username, password, role from user where username=1 and 1=1。
MyBatis容易产生问题的三个地方
1. like模糊查询
在Mybatis中使用like进行模糊查询:
Select * from news where title like ‘%#{title}%’
但是这种写法会报错抛异常,这种时候把#改成$能正常运行,但是也产生了安全问题;
正确写法:
select * from news where tile like concat(‘%’,#{title}, ‘%’)
2. in 之后的多个参数
in之后多个id查询时使用# 同样会报错
Select * from news where id in (#{ids})
正确用法为使用foreach,而不是将#简单替换为$
id in<foreach collection="ids" item="item" open="("separatosr="," close=")">#{ids} </foreach>
3. order by之后
Select * from news where title ='#{titlename}' order by #{time} asc
需要注意order by之后通过#执行也会报错,使用order by语句时是无法使用预编译的,原因是order by子句后面需要加字段名或者字段位置,而字段名是不能带引号的,否则就会被认为是一个字符串而不是字段名,然而使用PreapareStatement将会强制给参数加上',所以还要在过滤上做好防御的准备
正确写法:
Select * from news where title ='#{titlename}' order by ${time} asc
所以审计时候可以多观察这几个容易出现问题的地方。
总结:可以使用idea 搜索$关键字,先筛选xml文件搜索$,逐个分析,要特别注意mybatis-generator的order by注入,全局搜索调出Find in Path,筛选后缀xml,搜索$关键字,找到是mybatis的数据库文件,找到调用函数后,⌘+f7查看调用链,检查中间是否被过滤
参考
https://www.cnblogs.com/CoLo/p/15225346.html
https://www.cnblogs.com/nice0e3/p/13647511.html