任何一个Java中级开发,正常情况下都不应该会出现sql注入漏洞
漏洞描述
SQL注入攻击是黑客利用SQL注入漏洞对数据库进行攻击的常用手段之一。攻击者通过浏览器或者其他客户端将恶意SQL语句插入到网站参数中,网站应用程序未经过滤,便将恶意SQL语句带入数据库执行。
漏洞危害
SQL注入漏洞可能会造成服务器的数据库信息泄露、数据被窃取、网页被篡改,甚至可能会造成网站被挂马、服务器被远程控制、被安装后门等。
漏洞利用条件
SQL注入的分类较多,一般可笼统地分为数字型注入与字符串型注入两类;当然,也可以更加详细地分为联合查找型注入、报错注入、时间盲注、布尔盲注等。
造成SQL注入一般需要满足以下两个条件:
- 输入用户可控。
- 直接或间接拼入SQL语句执行。
常见审计思路
对于SQL注入漏洞审计,常见的方法是,根据SELECT、UPDATE等SQL关键字或是通过执行SQL语句定位到存在SQL语句的程序片段,随后通过查看SQL语句中是否存在变量的引用并跟踪变量是否可控。
因SQL注入漏洞特征性较强,在实际的审计过程中我们往往可以通过一些自动化审计工具快速地发现这些可能存在安全问题的代码片段。
如使用Fortify等自动化工具。Java语言本身是一种强类型语言,因此在寻找SQL注入漏洞的过程中,可以首先找到所有包含SQL语句的点,随后观察传参类型是否是String类型,只有当传参类型是String类型时我们才可能进行SQL注入。执行sql语句的几种方式
在Java中执行SQL语句一般有以下几种方式:
● 使用JDBC的java.sql.Statement执行SQL语句。
● 使用JDBC的java.sql.PreparedStatement执行SQL语句。
● 使用Hibernate执行SQL语句。
● 使用MyBatis执行SQL语句。Statement
Statement是Java JDBC下执行SQL语句的一种原生方式,执行语句时需要通过拼接来执行。若拼接的语句没有经过过滤,将出现SQL注入漏洞。PreparedStatement
PreparedStatement是继承statement的子接口,包含已编译的SQL语句。PreparedStatement会预处理SQL语句,SQL语句可具有一个或多个IN参数。IN参数的值在SQL语句创建时未被指定,而是为每个IN参数保留一个问号(?)作为占位符。每个问号的值,必须在该语句执行之前通过适当的setXXX方法来提供。如果是int型则用setInt方法,如果是string型则用setString方法。PreparedStatement预编译的特性使得其执行SQL语句要比Statement快,SQL语句会编译在数据库系统中,执行计划会被缓存起来,使用预处理语句比普通语句更快。PreparedStatement预编译还有另一个优势,可以有效地防止SQL注入攻击,其相当于Statement的升级版。MyBatis
MyBatis是一个Java持久化框架,它通过XML描述符或注解把对象与存储过程或SQL语句关联起来,它支持自定义SQL、存储过程以及高级映射。MyBatis封装了几乎所有的JDBC代码,可以完成设置参数和获取结果集的工作。
Mybatis使用#{}和${}两种方式来接收参数。
- #{}在底层实现上使用“?”作为占位符来生成PreparedStatement,也是参数化查询预编译的机制,这样既快又安全。
- #{}在底层实现上使用“?”作为占位符来生成PreparedStatement,也是参数化查询预编译的机制,这样既快又安全。
结论1
综上所述,只要使用PreparedStatement和mybatis的#{}方式,就可以避免sql注入。
但是,有的场景,不适合使用上面的方式例外-PreparedStatement
个人习惯
使用PrepareStatement执行SQL语句是因为预编译参数化查询能够有效地防止SQL注入。很多开发者因为个人开发习惯的原因,没有按照PrepareStatement正确的开发方式进行数据库连接查询,在预编译语句中使用错误编程方式,那么即使使用了SQL语句拼接的方式,同样也会产生SQL注入漏洞。
虽然id参数使用了PrepareStatement进行SQL查询,但是后面的username使用了SQL语句拼接的方式“sql+=”andusername like’%”+username+”%’”;”,将username参数进行了拼接,这样导致了SQL注入漏洞的产生。传入的username值为“”user%’or’1’=’1’#””//未完全使用预编译方式,习惯性使用拼接方式...String username = "user % ' or '1' = '1'# ";String id = "2";String sql = "select * from user where id = ?";sql = sql + "and username like '%" + username + "%";PreparedStatement p = conn.prepareStatement(sql);p.setString(1,id);...
因为原语句使用%进行模糊查询,这里会将user表中所有包含user的username字段数据输出select * from user where id = ? and username like '%user%' or '1' = '1' #%
order by注入
在预编译语句中按照规范正确编程就能防止SQL注入,但是有特殊情况,不能使用PrepareStatement,因为PrepareStatement是使用占位符传入参数的,传递的字符都会有单引号包裹,如:
SELECTFROM user order by ‘id’;
而正常的order by查询,则是没用单引号的,上面那个方式,order by 会失效
SELECTFROM user order by id;
...String id = "2 or 1 = 1";String sql = " select * from user " + " order by " + id ;PreparedStatement p = conn.prepareStatement(sql);...//最终查询语句为select * from user order by 2 or 1 = 1;
%和_模糊查询
在java预编译查询中不会对%和进行转义处理,而%和刚好是like查询的通配符,如果没有做好相关的过滤,就有可能导致恶意模糊查询,占用服务器性能,甚至可能耗尽资源,造成服务器宕机。
对于此攻击方式最好的防范措施就是进行过滤,此类攻击场景大多出现在查询的功能接口中,直接将%进行过滤就是最简单和有效的方式。
一般来说,很少有人去使用原生的方式写sql了,大部分应该都是使用mybatis或者mybatis plus
例外-Mybatis
order by查询
在前面order by注入中已经讲到,order by子句不能使用参数化查询的方式,只能使用字符拼接的方式,而在MyBatis中#{}是进行参数化查询的,如果在MyBatis的order by子句中使用#{},则order by子句会失效,但${}可能会存在SQL注入漏洞,要避免SQL注入漏洞就要进行过滤。
//某个网友的思路,对order by 字段进行白名单过滤SELECT * FROM alarm_list_info_view as m<choose><when test="(orderBy=='begin_time' or orderBy=='update_time') and (sort=='desc' or sort=='asc')">order by ${orderBy} ${sort}</when><otherwise>order by updateTime desc</otherwise></choose>原文链接:https://blog.csdn.net/qq_36735986/article/details/120552218
like查询
MyBatis的like子句中使用#{}程序会报错,为了避免报错只能使用${}。
select * from user where name like'%${user}%'
但${}可能会存在SQL注入漏洞,所以要写成下面这个方式
select * from user where name like concat('%', #{name}, '%')
in参数
in之后多个id查询时使用# 同样会报错
Select * from news where id in (#{ids})
使用foreach,而不是使用${}
Select * from news where id in<foreach collection="ids" item="item" open="("separatosr="," close=")">#{ids}</foreach>
结论2
使用PreparedStatement和mybatis的#{}方式,不能完全避免sql注入。因为有些特殊情况,导致于不能正确的使用预编译。
结论3
根据上面的信息,我们可以知道一些会出现sql注入的地方:
- 直接使用Statement进行sql语句的执行。
- 使用PreparedStatement时,个人习惯导致的拼接sql
- 使用PreparedStatement时,order by 子句未过滤
- 使用PreparedStatement时,模糊查询未过滤
- mybatis 中使用${},如order by 、 like 、 in 。
常见关键字
通过常见关键字,可以快速找到sql语句,进而对这些语句进行审计,查看是否有sql注入的存在
- Statement
- creatStatement
- PrepareStatement
- like ‘% ${
- in (${
- select
- update
- insert
