大家好,如何避免SQL注入漏洞相信很多的网友都不是很明白,包括也是一样,不过没有关系,接下来就来为大家分享关于如何避免SQL注入漏洞和的一些知识点,大家可以关注收藏,免得下次来找不到哦,下面我们开始吧!
SQL语句的执行过程中,需要经过以下三个基本步骤:
代码语义分析制定执行计划以获得返回结果。 SQL语句由两部分组成:代码和数据,例如:
SELECT id、姓名、电话FROM userTable WHERE name=’xiaoming’;
SELECT id、name、phone FROM userTable WHERE name=是代码,’xiaoming’ 是数据。
预编译,以Mybatis为例,就是用占位符预解析语义:
例如,SELECT id、姓名、电话FROM userTable WHERE id=#{name};
然后将数据“xiaoming”传递到占位符中。这样,如果代码语义分析阶段错开,就不会被误认为是代码的一部分。
最早,开发人员明确使用JDBC 来创建连接并自行执行SQL 语句。这种情况下,如果没有经过充分的过滤就将外部可控数据拼接成SQL语句,就会出现漏洞。这种情况在正常的业务发展过程中并不多见。根据公司规定,如果没有特殊情况,必须使用ORM框架来执行SQL。
但在目前的一些项目中,仍然使用JDBC来编写一些工具脚本,例如DataMerge.java和DatabaseClean.java。利用JDBC 的灵活性,通过这些脚本来执行数据库批量操作。
此类代码不应出现在在线版本中,以免因各种情况而被外部调用。
三 直接使用Mybatis
1 易出错点
目前大部分平台代码都是基于Mybatis来处理持久层和数据库的交互。 Mybatis 有两个占位符{} 和#{} 用于传入数据。 {}和{}。 {}可以理解为语义分析之前的字符串拼接。传入的参数是原样传入的。
例如
从userTable WHERE name=’${name}’ 中选择ID、姓名、电话;传入name=xiaoming后,相当于SELECT id, name, Phone FROM userTable WHERE name=’xiaoming’;
在实际应用中
SELECT id、姓名、电话FROM userTable WHERE ${col}=’xiaoming’;传入col=’name’ 相当于SELECT id, name, Phone FROM userTable WHERE name=’xiaoming’;
正如预编译原理介绍中提到的,使用#{}占位符可以消除注入问题。但有一些业务场景是不能直接使用#{}的。
例如,按语法顺序
如果写SELECT id, name, Phone FROM userTable ORDER BY #{};执行时会报错。因为order by后面的内容是列名,是代码语义的一部分。如果在语义分析部分没有确定,则相当于执行SELECT id, name, Phone FROM userTable ORDER BY。难免会有语法错误。
另一个例子是类似的场景
SELECT id, name, Phone FROM userTable WHERE name like ‘%#{name}%’;#{} 将不会被解析,从而导致错误。
无论是in 语法还是Between 语法都是如此,那么如何解决这类问题呢?
2 正确书写
在order by(group by)语句中使用${}
1.使用条件判断
2、利用全局过滤机制,限制order by后的变量内容只能是数字、字母、下划线。
如果您使用常规过滤:
关键字=关键字.replaceAll(‘[^a-zA-Z0-9_\s+]’, ”);这里需要注意的是,过滤需要使用白名单,不能使用黑名单。黑名单并不能解决注入问题。
喜欢声明
由于like中的关键字需要用两个%符号包裹起来,所以可以使用CONCAT函数进行拼接。
请注意不要使用CONCAT(‘%’,’${stuName}’,’%’) ,因为仍然存在漏洞。也就是说,使用$符号是不正确的,使用#符号是安全的。
IN 语句
与like语句类似,直接使用#{}会导致错误。常见错误如下:
(${tenantIds})中tenant_id的正确写法是:
四 Mybatis-generator使用安全
在繁重的CRUD代码压力下,开发者慢慢开始通过Mybatis-generator、idea-mybatis-generator插件、通用Mapper、Mybatis-generator-plus自动生成Mapper、POJO、Dao等文件。
这些工具可以自动生成CRUD所需的文件,但如果使用不当,就会自动出现SQL注入漏洞。我们以最常用的org.mybatis.generator为例来说明可能出现的问题。
1 动态语句支持
Mybatis-generator提供了一些功能来帮助用户连接SQL的各种条件,例如多参数的like语法、多参数的比较语法等。为了保证使用的简单性,需要将一些语义代码拼接成SQL语句。并且如果开发者使用不当,外部输入也会传递到{} 占位符中。会有漏洞。
2 targetRuntime参数配置
配置生成器时,配置文件generator-rds.xml中有一个targetRuntime属性,默认为MyBatis3。在这种情况下,Mybatis的动态语句支持将被启用,并且enableSelectByExample、enableDeleteByExample、enableCountByExample和enableUpdateByExample函数将被启用。
以enableSelectByExample为例,xml映射文件中将替换以下动态模块:
开发者可以通过包含该模块来添加where条件,但如果使用不当,会导致SQL注入漏洞:
并添加带有自定义参数的函数:
公共条件addKeywordTo(String 关键字) { StringBuilder sb=new StringBuilder(); sb.append(‘(display_name like ‘%’ + 关键字+ ‘%’ 或’); sb.append(‘org like ” + 关键字+ ‘%’ 或’); sb.append(‘status like ‘%’ + 关键字+ ‘%’ 或’); sb.append(‘id like ” + 关键字+ ‘%’) ‘); addCriterion(sb.toString()); return (Criteria) this;}目的是同时实现对display_name、org、status、id的类似操作。其中addCriterion是Mybatis-generator自带的函数:
protected void addCriterion(String condition) { if (condition==null) { throw new RuntimeException(‘条件的值不能为null’); } criteria.add(new Criterion(condition));} 这里的误解是addCriterion本身提供了对多个条件的支持,但开发者认为需要将多个条件拼接在一起,一起传入addCriterion方法。就像本例中的代码一样,最终传递给addCriterion 的参数只有一个。因此执行Example_Where_Clause语句:
也就是说,开发者直接将他拼接的SQL语句代入了${criterion.condition},从而导致了该漏洞。根据Mybatis-generator的文档,正确的写法应该是:
public void addKeywordTo(String keywords, UserExample userExample) { userExample.or().andDisplayNameLike(‘%’ + keywords + ‘%’); } userExample.or().andOrgLike(关键字+ ‘%’); userExample.or() .andStatusLike(‘%’ + 关键字+ ‘%’); userExample.or().andIdLike(‘%’ + keywords + ‘%’);}or方法负责创建Criteria,此时触发的逻辑为
${criterion.condition} 替换为不带单引号的like。 Like是语义代码,在语义分析之前拼接到SQL语句中,’%’+关键字+’%’会作为数据#{criterion.value}加入到预编译中,以避免注入。
同样,还提供了In 语法的安全使用:
安全使用Beetween 的方法:
example.or() .andField6Between(3, 7); Mybatis-generator默认生成的order by语句也是直接使用${}拼接的:
如果不对传入参数进行额外的过滤,就会导致注入问题。
3 订购依据
除了我自己写的SQL语句外,Mybatis-generator默认生成的order by语句也是直接使用${}拼接的:
如果不对传入参数进行额外的过滤,就会导致注入问题。
PS: 在实际扫雷过程中,发现很多语句自动生成order by语法,但上层调用时,没有传入可选参数。这种情况下应该去掉多余的order by语法。
4个其他插件
插件之间的安全漏洞并不相同。以下是常用插件的简要列表。
想法-mybatis-生成器
这是IDEA的插件,可以在开发过程中从IDE层面自动生成CRUD所需的文件。使用此插件时还需要注意一些默认的安全风险。
1)加工定制订单
like\in\ Between 可以参考官方文档使用,不存在安全风险。
不过该插件没有内置order by处理,需要自己编写。编写时参考Case2
2)默认IF条件之前,需要判断是否为空。
插件生成的默认语法大致如下:
当ID参数为空时,if标签下的逻辑不会添加到SQL语句中,可能会导致DOS、权限绕过等漏洞。因此,在将参数传入查询语句之前,需要确认参数不为空。
宝米豆.mybatis-plus
在apply方法中传递参数时,应该使用最后一个带有{}的方法。其原理是直接拼接到SQL语句末尾,可能会造成注入漏洞。
五 其它ORM框架
1休眠
ORM 代表对象关系映射。简单来说,它将数据库中的表映射到Java对象。这种只有属性而没有业务逻辑的对象也称为POJO(Plain Ordinary Java Object)对象。
Hibernate 是第一个广泛使用的ORM 框架。它通过XML管理数据库连接,提供全表映射模型,具有高度的封装性。配置好映射文件和数据库链接文件后,Hibernate就可以通过Session对象进行数据库操作了。开发者不需要接触SQL语句,只需要编写HQL语句。
Hibernate经常与Struts和Spring结合使用,这是Java世界中经典的SSH框架。
与SQL相比,HQL的语法限制更多:
无法查询未映射的表。只有当模型之间的关系明确时才能使用UNION语法。表名和列名区分大小写。不*,, – 。没有延时功能。因此,HQL注入比SQL注入更难利用。从代码审计的角度来看,与普通的SQL注入是一致的:
拼接可能会导致注入漏洞:
您可以使用占位符和命名参数来阻止SQL 语句,这些语句本质上是预编译的。
Hibernate在使用过程中存在很多缺点:
全表映射不灵活,更新时需要发送所有字段,影响程序运行效率。对复杂查询的支持很差。对存储过程的支持很差。 HQL性能较差,无法基于SQL进行优化。在审计Hibernate相关注入时,可以通过全局搜索createQuery快速定位SQL操作的位置。
2JPA
JPA,全称Java Persistence API,是Java EE提供的数据持久化规范,允许开发者通过XML或者注解的方式将一个对象持久化到数据库中。
主要包括三个方面:
1、ORM映射元数据通过XML或注解描述对象和数据表之间的对应关系。框架可以自动将对象中的数据保存到数据库中。
常见的注解有:@Entity、@Table、@Column、@Transient
2、数据操作API,内置接口,方便对某个数据表进行CRUD操作,节省开发者编写SQL的时间。
常用方法有:entityManager.merge(T t);
3.JPQL,提供了一种面向对象而不是面向数据库的查询语言,将程序与数据库和SQL解耦。
JPA是一组规范,Hibernate实现了这个JPA规范。
在Spring框架中,提供了一个简单版本的JPA实现——spirng data jpa。通过按照约定的方法命名规则编写dao层接口,就可以在不编写接口实现的情况下访问和操作数据库。它还提供了很多CRUD以外的功能,比如分页、排序、复杂查询等,使用起来比较简单,但是仍然使用Hibernate底层的JPA实现。
就像HQL注入一样,如果使用拼接的方式将用户可控的数据替换到查询语句中,就会导致SQL注入。
安全查询应该使用预编译技术。
Spring Data JPA的预编译编写方法为:
String getUser=’从用户中选择用户名,其中id=?’;查询query=em.createNativeQuery(getUser);query.setParameter(1, id);String username=query.getResultList(); Tips:其实Hibernate的出现日期早于JPA规范。 Hibernate逐渐成熟后,JavaEE开发团队邀请Hibernate核心开发人员制定JPA规范。之后Spring Data JPA根据规范进一步优化。另外,还有很多实现JPA规范的产品,例如Eclipse的TopLink(OracleLink)。
六 总结
经过上面的介绍,尤其是围绕Mybatis容易出错点的讨论,我们可以得出以下结论:
持久层组件有多种类型。开发人员对工具使用的误解是造成漏洞的主要原因。由于自动生成插件的动态特性,SQL漏洞的自动发现不能简单地使用${}来发现。必须根据全局持久层组件特性制定详细的匹配规则。参考链接:
原创文章,作者:小su,如若转载,请注明出处:https://www.sudun.com/ask/143945.html
用户评论
ゞ香草可樂ゞ草莓布丁
以前写web程序没有意识到SQL注入的重要性,看了这篇博文才明白其中的危害很大!真想立刻就去修改我的项目安全机制了,谢谢分享这个很实用的技巧!
有8位网友表示赞同!
£烟消云散
这篇文章写的太棒了!把SQL注入的原理和预防方法都说得清清楚楚,易懂又详细。我终于理解了为什么我们要这么重视这个问题了
有18位网友表示赞同!
非想
虽然现在开发框架比较成熟,很多都会自动处理这个问题,但还是得了解一下底层原理,以免掉进陷阱。这篇文章对入门者很有帮助!
有17位网友表示赞同!
苏莫晨
实际开发中有很多其他安全问题需要注意,不能只关注SQL注入。比如密码加密、数据传输安全等等都不可忽视啊,这篇文章只能算是安全学习的一个小小切入点…
有11位网友表示赞同!
终究会走-
说那么多方法,还是觉得最安全的直接使用ORM工具吧!框架自动处理的问题就少很多,可以把精力集中在业务逻辑上
有17位网友表示赞同!
珠穆郎马疯@
说得太笼统了,具体哪个框架,哪个数据库需要怎样操作才能避免注入?这些细节都没有讲到…
有13位网友表示赞同!
十言i
感觉这篇文章偏基础,我之前已经接触过相关的安全知识了。希望以后能分享一些更深入的防御技术!
有19位网友表示赞同!
醉红颜
学习到了很多!以前以为SQL注入很简单就能解决,没想到这么复杂。真的要好好加强安全意识和学习
有13位网友表示赞同!
强辩
这篇博文真是打开了我的眼界,原来程序的安全性问题这么多,我以后写代码的时候一定要更加注意安全!这篇文章让我受益匪浅
有8位网友表示赞同!
桃洛憬
总觉得SQL注入这类漏洞出现的概率很低,毕竟现在框架已经很完善了。可能是我太过于乐观了吧…还是要多学习安全方面的知识
有11位网友表示赞同!
娇眉恨
为了程序安全,确实需要仔细对待数据库的安全连接和数据输入的处理方式,这篇博文提醒了我这个重要问题!
有12位网友表示赞同!
心贝
虽然文章说的对,但是一些实际案例分析能更加直观地展示 SQL注入带来的危害,会更有说服力
有12位网友表示赞同!
爱你的小笨蛋
写代码时注意输入数据预处理,避免直接拼接用户输入到 SQL 查询语句中。这是避免 SQL 注入漏洞的最佳方法!
有17位网友表示赞同!
为爱放弃
很多时候开发人员为了追求效率就忽略了安全问题,导致一些潜在漏洞,希望以后能重视这些细节,保证程序安全运行。
有12位网友表示赞同!
采姑娘的小蘑菇
感觉这个标题说的不全吧?毕竟还有其他的漏洞类型也需要避免
有9位网友表示赞同!
心安i
如果想要防止SQL注入漏洞,建议使用 parameterized queries 或 prepared statements。这种方法可以有效地隔离用户输入和 SQL 语句!
有15位网友表示赞同!
别伤我i
这篇博文提醒了开发者要更加注意安全问题,并且给出了一些具体的预防措施,很有帮助!希望更多开发者都能重视这个问题,共同打造更安全可靠的应用程序环境!
有13位网友表示赞同!