随着互联网的发展,Java语言被广泛应用于金融服务、电子商务、大数据技术等领域。Java安全编码约定长期以来一直是SDL不可或缺的一部分。本文以Java项目中广泛使用的两个框架Hibernate和MyBatis为例,介绍在编码过程中如何避免SQL注入的几种编码方式,包括对预编译的深入剖析以及理解预编译的几个“误区”。》解释。备注,本文是Java语言安全编码协会系列文章的第一篇。一、框架介绍Hibernate和MyBatis是目前java项目广泛使用的两个框架。由于Hibernate简单易用,Hibernate被广泛使用之前的项目中使用过,但是由于Hibernate的侵入性,后来逐渐被MyBatis所取代。接下来我们将基于SpringBoot搭建Hibernate和MyBatis的漏洞环境。2.配置说明SpringBoot使用2.3.1.RELEASE,MySQL版本是5.7.20,数据库有一个表user_tbl,数据如下:3.HibernateHibernate是一个开源的对象关系映射框架,它用非常轻量级的对象封装了JDBC,是一个全自动的ORM框架。自动生成SQL语句并自动执行(1)环境结构如下,ctl为控制层,service为服务层,一个nddao是持久层。为了方便非标准接口实现,我们只关注漏洞部分。Beans下的user.java作为user_tbl表结构。我们使用/inject接口,p是接受外部参数查询用户列表,使用fastjson格式化输出。让我们回到dao层。1)SQL注入对于SQL注入,我们使用字符串拼接:访问http://localhost:8080/inject?p=m,直接用SQLMap运行:注入数据很简单。2)HQL注入HQL(HibernateQueryLanguage)是Hibernate专门用来查询数据的语句。与SQL不同,HQL更接近于面向对象的思维方式。表名对应我们上面的实体配置。HQL注入的使用比SQL注入的使用更难。比如一般的程序员不会映射系统表,所以通过系统表获取属性几乎是不可能的。同时,由于HQL对复杂语句的支持较差,攻击者举步维艰。构建可用有效负载需要更多时间。更详细的语法可以参考:https://docs.huihoo.com/Hibernate/reference-v3_zh-cn/queryhql.html3)预编译我们使用setParameter的方式,也是我们熟悉的预编译方式。Queryquery=(Query)this.entityManager.createQuery("fromUseruwhereu.userNamelike:userName",User.class);query.setParameter("userName","%"+username+"%");访问http://localhost:8080/inject?p=m后,得到正常结果。执行注入语句:http://localhost:8080/inject?p=m'or‘1’like‘1,returnisempty.让我们看一下setParameter方法对我们的SQL语句做了什么。我们将断点设置为Loader.class的bindPreparedStatement。发现预编译后SQL变成了:selectuser0_.idasid1_0_,user0_.passwordaspassword2_0_,user0_.usernameasusername3_0_fromuser_tbluser0_whereuser0_.usernamelike'%''or''1''like''1%',然后交给hikari处理。发现我们的单引号变成了两个单引号,也就是说传入的数据变成了字符串。断点到mysql-connector-java(也就是我们熟悉的JDBC驱动包)的ClientPreparedQueryBindings.setString。这是设置参数的地方。看下算法:StringparameterAsString=x;booleanneedsQuoted=true;if(this.isLoadDataQuery||this.isEscapeNeededForString(x,stringLength)){needsQuoted=false;StringBuilderbuf=newStringBuilder((int)((double)x.length()*1.1D));buf.append('\'');for(inti=0;i
