sql注入

检查点:数据库查询

前言:

在 Java 中,操作SQL的主要有以下几种方式:
  1. java.sql.Statement
  2. java.sql.PrepareStatement
  3. 使用第三方 ORM 框架 —— MyBatis 或 Hibernate
下面我们来分析以上几种执行SQL的方式。
 

java.sql.Statement

java.sql.Statement 是最原始的执行SQL的接口,使用它时,需要手动拼接SQL语句,如下面这样:
sql注入
构造请求 /?id='or 1 #,服务器将 'or 1 # 拼接到 sql 语句中,就会变成 SELECT * FROM user WHERE username = ''or 1 #,将返回 user 表的所有记录。故不推荐此写法。sql注入
 
 

java.sql.PrepareStatement

这个接口是对 java.sql.Statement 的拓展,拥有了防SQL注入的特性。
Tip: java.sql.Statement 每次执行一条SQL,都要重新编译一次SQL。而 java.sql.PreparedStatement 预编译的方式,会将SQL缓存在数据库,可以重复调用,相比 Statement 效率要高一些。
使用时,在SQL语句中, ? 作为占位符,代替需要传入的参数,然后将该语句传递给数据库,数据库会对这条语句进行预编译。如果要执行这条SQL,只要用特定的 set 方法,将传入的参数设置到SQL语句中的指定位置,然后调用 execute 方法执行这条完整的SQL,也就是参数化查询语句。以javasec示例如下:
sql注入
预处理的修复原理:针对字符串类型的SQL注入,是在字符串两边加上一对单引号'',对于中间点的单引号对其进行转义\'
sql注入
Mybatis的#{}也是预处理方式处理SQL注入。
此时,如果我用之前的请求攻击,执行的SQL会变成 SELECT * FROM user WHERE username = '\'or 1 #',可以看到单引号是被转义了,同时参数也被一对单引号包裹,数字型注入也不存在了。
 

处理方法:

1、所以对于sql注入采用预编译即可。参数化查询。
2、对于mybatis则可添加全局过滤器,过滤特殊字符。
Web.xml:
sql注入
SQLFilter.java中:
3、修改数据类型
变量 id的数据类型是 string 型,而其实对于这个用户信息查询的功能来说,只需要一个整数型的数字即可,因此可以修改变量 id 的数据类型,来达到修复的目的: 当然,这种方法只限定于特定情况下,如果有的参数其数据类型必须为 String 那么这种方法是行不通的。
如 Mybatis 框架中的 like、 in和 order by 语句、Hibernate 框架中的 createQuery()函数等,如果使用不当,依旧可能造成 sql 注入。
补充:
Mybatis的#{}也是预处理方式处理SQL注入。
在使用了mybatis框架后,需要进行排序功能时,在mapper.xml文件中(mybatis中Mapper映射文件)编写SQL语句时,注意orderBy后的变量要使用${},而不用#{}
因为#{}变量是经过预编译的,
${}没有经过预编译。
虽然${}存在SQL注入的风险,但orderBy必须使用${}因为#{}会多出单引号''导致SQL语句失效。为防止SQL注入只能自己对其过滤。 一些能防御SQL 注入的 API。
根据下面的结果可以发现order by 'username'并没有用,第一条SQL和第二条SQL效果一样。
select * from users order by 'username' desc -- 结果为 joychou wilson lightless,并没有用
select * from users                          -- 结果为 joychou wilson lightless 
select * from users order by username        -- 结果为 joychou lightless wilson
select * from users order by username desc   -- 结果为 wilson lightless joychou
 
sql注入
实际案例分析(CVE-2019-9615):待完善
 

参考链接:

从1开始的Java代码审计·第三弹·SQL注入

园长的github关于sql-inject

补充链接:

Mybatis中mapper映射文件详解

Mybatis中mapper映射文件中的#{}与${}