【转载】以Java的视角来聊聊SQL注入

以Java的视角来聊聊SQL注入
原创 2017-08-08 javatiku  Java面试那些事儿
在大二就接触过sql注入,以前一直在学习windows逆向技术,认为web安全之后不是本身的从业方向,因此当时也就没有深刻研究。工做多年来,本人也一直从事安全开发相关工做,随着Java的市场份额愈来愈重,在工做中接触Java的机会也愈来愈多,也是机缘巧合的契机,本身开始走向了偏 Java开发的道路。最近工做中接触到一个项目,其代码风格极其不堪入目,更严重的是DAO部分存在大量SQL注入的隐患,因此趁这个机会,做者复习研究了一把SQL注入相关的知识,在这里与你们探讨一下。
0
什么是SQL注入
SQL注入是影响企业运营最具备破坏性的漏洞之一。
应用程序向后台数据库进行SQL查询时,若是为攻击者提供了影响该查询的能力,就会引发SQL注入。
 
1
靶场准备
首先咱们来准备一个web接口服务,该服务能够提供管理员的信息查询,这里咱们采用springboot + jersey 来构建web服务框架,数据库则采用最经常使用的mysql。下面,咱们来准备测试环境,首先创建一张用户表jwtk_admin,SQL以下:
而后插入默认的管理员:
这样咱们就有了两位系统内置管理员了,管理员密码采用MD5进行Hash,固然这是一个很简单的为了做为研究靶场的表,因此没有很全的字段。
 
接下来,咱们建立 spring boot + jersey 构建的RESTFul web服务,这里咱们提供了一个经过管理员用户名查询管理员具体信息的接口,以下:
 
2
SQL注入测试
首先咱们以开发者正向思惟向web服务发送管理员查询请求,这里咱们用PostMan工具发送一个GET请求,请求与结果以下图所示:
不出咱们和开发者所料,Web接口返回了咱们想要的结果,用户名为admin的管理员信息。OK,如今开发任务完成,Git Push,Jira任务点为待测试,那么这样的接口就真的没有问题了吗?如今咱们发送这样一条GET请求:
发送该请求后,咱们发现PostMan没有接收到返回结果,而Web服务后台却开始抛MySQLSyntaxErrorException异常了,错误以下:
 
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''xxxx''' at line 1
 
缘由是在咱们查询的 xxxx' 处sql语句语法不正确致使。这里咱们先不讨论SQL语法问题,咱们继续实验,再次构造一条GET查询请求:
此时,咱们能够惊讶的发现,查询接口非但没有报错,反而将咱们数据库jwti_admin表中的全部管理员信息都查询出来了:
这是什么鬼,难道管理员表中还有 name=xxxx'or'a'='a 的用户?这就是 SQL Injection。
 
3
注入原理分析
在接口中接受了一个String类型的name参数,而且经过字符串拼接的方式构建了查询语句。在正常状况下,用户会传入合法的name进行查询,可是黑客却会传入精心构造的参数,只要参数经过字符串拼接后依然是一句合法的SQL查询,此时SQL注入就发生了。正如咱们上文输入的name=xxxx'or'a'='a与咱们接口中的查询语句进行拼接后构成以下SQL语句:
 
当接口执行此句SQL后,系统后台也就至关于拱手送给黑客了,黑客一看到管理员密码这个hash,都不用去cmd5查了,直接就用123456密码去登陆你的后台系统了。Why?由于123456的md5哈希太常见了,别笑,这就是不少中小网站的现实,弱口令横行,不见棺材不落泪!
 
好了,如今咱们应该明白了,SQL Injection缘由就是因为传入的参数与系统的SQL拼接成了合法的SQL而致使的,而其本质仍是将用户输入的数据当作了代码执行。在系统中只要有一个SQL注入点被黑客发现,那么黑客基本上能够执行任意想执行的SQL语句了,例如添加一个管理员,查询全部表,甚至“脱裤” 等等,固然本文不是讲解SQL注入技巧的文章,这里咱们只探讨SQL注入发生的缘由与防范方法。
 
4
JDBC的预处理
在上文的接口中,DAO使用了比较基础的JDBC的方式进行数据库操做,直接使JDBC构建DAO在比较老的系统中仍是很常见的,但这并不意味着使用JDBC就必定不安全,若是我将传入的参数  xxxx'or'a'='a 总体做为参数进行name查询,那就不会产生SQL注入。在JDBC中,提供了PreparedStatement (预处理执行语句)的方式,能够对SQL语句进行查询参数化,使用预处理后的代码以下:
一样,咱们使用上文的注入方式注入 ,此时咱们发现,SQL注入没能成功。如今,咱们来打印一下被被预处理后的SQL,看看有什么变化:
看到了吗?全部的  '  都被 \' 转义掉了,从而能够确保SQL的查询参数就是参数,不会被恶意执行,从而防止了SQL注入。
 
5
Mybatis下注入防范
MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架, 其几乎避免了全部的 JDBC 代码和手动设置参数以及获取结果集。同时,MyBatis 能够对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录,所以mybatis如今在市场中采用率也很是高。这里咱们定义以下一个mapper,来实现经过用户名查询管理员的接口:
一样提供Web访问接口:
接下来,咱们尝试SQL注入name字段,能够发现注入并无成功,经过打印mybatis的Log能够看到mybatis框架对参数进行了预处理处理,从而防止了注入:
那是否只要使用了mybatis就必定能够避免SQL注入的危险?咱们把mapper作以下修改,将参数#{name}修改成${name},并使用name='xxxx' or 'a'='a' 做为GET请求的参数,能够发现SQL注入仍是发生了:
那这是为何,mybatis ${}与#{}的差异在哪里?
原来在mybatis中若是以${}形式声明为SQL传递参数,mybatis将不会进行参数预处理,会直接动态拼接SQL语句,此时就会存在被注入的风险,因此在使用mybatis做为持久框架时应尽可能避免采用${}的形式进行参数传递,若是没法避免(有些SQL如like、in、order by等,程序员可能依旧会选择${}的方式传参),那就须要对传入参数自行进行转义过滤。
6
JPA注入防范
JPA是Sun公司用来整合ORM技术,实现天下归一的ORM标准而定义的Java Persistence API(java持久层API),JPA只是一套接口,目前引入JPA的项目都会采用Hibernate做为其具体实现,随着无配置Spring Boot框架的流行,JPA愈来愈具备做为持久化首选的技术,由于其能让程序员写更少的代码,就能完成现有的功能,例如强大的JpaRepository,常规的SQL查询只需按照命名规则定义接口,即可以不写SQL(JPQL/SQL)就能够实现数据的查询操做,从SQL注入防范的角度来讲,这种将安全责任抛给框架远比依靠程序员自身控制来的保险。所以若是项目使用JPA做为数据访问层,基本上能够很大程度的消除SQL注入的风险。可是话不能说的太死,在我见过的一个Spring Boot项目中,虽然采用了JPA做为持久框架,可是有一位老程序员不熟悉于使用JPQL来构建查询接口,依旧使用字符串拼接的方式来实现业务,而为项目安全埋下了隐患。
安全须要一丝不苟,安全是100 - 1 = 0的业务,即便你防护了99%的攻击,那还不算胜利,只要有一次被入侵了,那就有可能给公司带来很严重的后果。
关于JPA的SQL注入,咱们就不详细讨论了,由于框架下的注入漏洞属于框架漏洞范畴(如CVE-2016-6652),程序员只要遵循JPA的开发规范,就无需担忧注入问题,框架都为你作好幕后工做了。
 
7
SQL注入的其余防范办法
不少公司都会存在老系统中有大量SQL注入风险代码的问题,可是因为其已稳定支持公司业务好久,不宜采用大面积代码更新的方式来消除注入隐患,因此须要考虑其采用他方式来防范SQL注入。除了在在SQL执行方式上防范SQL注入,不少时候还能够经过架构上,或者经过其余过滤方式来达到防止SQL注入的效果。
  • 一切输入都是不安全的:对于接口的调用参数,要进行格式匹配,例如admin的经过name查询的接口,与之匹配的Path应该使用正则匹配(由于用户名中不该该存在特殊字符),从而确保传入参数是程序控制范围以内的参数,即只接受已知的良好输入值,拒毫不良输入。注意:验证参数应将它与输出编码技术结合使用。
  • 利用分层设计来避免危险:前端尽可能静态化,尽可能少的暴露能够访问到DAO层的接口到公网环境中,若是现有项目,很难修改存在注入的代码,能够考虑在web服务以前增长WAF进行流量过滤,固然代码上就不给hacker留有攻击的漏洞才最好的方案。也能够在拥有nginx的架构下,采用OpenRestry作流量过滤,将一些特殊字符进行转义处理。
  • 尽可能使用预编译SQL语句:因为动态SQL语句是引起SQL注入的根源。应使用预编译语句来组装SQL查询。
  • 规范化:将输入安装规定编码解码后再进行输入参数过滤和输出编码处理;拒绝一切非规范格式的编码。
相关文章
相关标签/搜索