Java代码审计安全篇-常见Java SQL注入
前言:
堕落了三个月,现在因为被找实习而困扰,着实自己能力不足,从今天开始 每天沉淀一点点 ,准备秋招 加油
注意:
本文章参考qax的网络安全java代码审计,记录自己的学习过程,还希望各位博主 师傅 大佬 勿喷,还希望大家指出错误
1.SQL语句参数直接动态拼接
常见的的执行语句为Statement执行SQL语句
例如下面这段代码:
<% //驱动程序名
String driverName = "com.mysql.jdbc.Driver";
//数据库用户名
String userName = "root";
//密码
String userPasswd = "root";
//数据库名
String dbName = "javasqltest";
//表名
String ID = '1'
//联结字符串
String url = "jdbc:mysql://localhost:3306/" + dbName + "?user="
+ userName + "&password=" + userPasswd;
Class.forName("com.mysql.jdbc.Driver").newInstance();
Connection connection = DriverManager.getConnection(url);
Statement statement = connection.createStatement();
String sql = "SELECT * FROM people where id = " +ID ;
ResultSet rs = statement.executeQuery(sql);
%>
打印出来的结果如图下所示
数据库数据:
我们发现 像这种情况就是由SQL语句参数直接动态拼接成的,这样的话参数ID可控并且可以执行sql语句的拼接,存在明显的SQL注入漏洞
String sql = "SELECT * FROM people where id = " +ID ;
我们测试验证 一下 传入参数为
1 and 1= 1
果然存在漏洞,这样就有个严重 的问题 当输入1 or 1 = 1就会打印所有的表的数据 ,造成数据泄露
1 or 1=1
2.预编译有误
首先预编译处理就是将一些灵活的参数值以占位符?的形式给代替掉,我们把参数值给抽取出来,把SQL语句进行模板化。让MySQL服务器执行相同的SQL语句时,不需要在校验、解析SQL语句上面花费重复的时间
public void test1() throws Exception {
// 获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?useServerPrepStmts=true", "root", "admin");
String sql = "select * from user where id = ?";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setInt(1, 1);
// 执行查询,获取结果集
ResultSet rs = ps.executeQuery();
//遍历查询结果集
while (rs.next()) {
System.out.println(rs.getObject("id")+"---"+rs.getObject("username"));
}
rs.close();
ps.close();
}
那么预编译有误这个很好理解,就是使用错了编程方式,也就是采用了动态拼接
上述代码就是典型的预编译错误编程方式,虽然id参数使用了PrepareStatement进行SQL查询,但是后面的usermame使用了SQL语句拼接的方式“sql += " and usermame like%”+username +.""%"";"",将username参数进行了拼接,这样导致了SQL注入漏洞的产
生。传入的username值为“"user% or T'=1"#""
3.order by 注入
就是即使是标准的预编译处理也难以防止sql注入,因为某些情况下是不能使用预编译处理的,只能采用字符串拼接处理 像order by 注入
原理:
SQL中:ORDER BY执行排序后面需要指定列名,该列名是不能被引号包含的否则就会被认为是一个字符串。
所以进行ID拼接的时候就出现了 sql注入
4. %和_模糊查询
首先明白这两个通配符的作用
_:任意一个字符,
%:任意0或多个字符。
下面举例 例如查询名字为root的用户 得到
我们填入r试试
查询不到 但是因为在java中预编译查询不会对%和_ 进行转义,而它们又是like查询的通配符,所以出现了漏洞,我们输入
username=r%
能够得到root的全部信息
所以只能手动过滤_和%
5.MyBatis中的#{}和${}
1、#{}表示一个占位符号 相当于 jdbc中的 ? 符号
#{}实现的是向prepareStatement中的预处理语句中设置参数值,sql语句中#{}表示一个占位符即?
2、${}将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:select * from user where id= #{user_id},如果传入的值是11,那么解析成sql时的值为where id="11" ,
就是说使用#{}方法更为安全 ${}方法不安全 ,因为在MyBatis中像order by 查询,like查询,in参数等都只能采用拼接方式 ,要确切的查询只能用到${},所以就会出现sql注入 ,所以得采用手动过滤的方法进行修复