• 近期将进行后台系统升级,如有访问不畅,请稍后再试!
  • 极客文库-知识库上线!
  • 极客文库小编@勤劳的小蚂蚁,为您推荐每日资讯,欢迎关注!
  • 每日更新优质编程文章!
  • 更多功能模块开发中。。。

以Java的视角来聊聊SQL注入

在大二就接触过 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 如likeinorder 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 查询。
  • 规范化:将输入安装规定编码解码后再进行输入参数过滤和输出编码处理;拒绝一切非规范格式的编码。

丨极客文库, 版权所有丨如未注明 , 均为原创丨
本网站采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行授权
转载请注明原文链接:以 Java 的视角来聊聊 SQL 注入
喜欢 (0)
[247507792@qq.com]
分享 (0)
勤劳的小蚂蚁
关于作者:
温馨提示:本文来源于网络,转载文章皆标明了出处,如果您发现侵权文章,请及时向站长反馈删除。

欢迎 注册账号 登录 发表评论!

  • 精品技术教程
  • 编程资源分享
  • 问答交流社区
  • 极客文库知识库

客服QQ


QQ:2248886839


工作时间:09:00-23:00