使用Hibernate防止SQL注入的方法

以前写代码,日后台传入一个组织好的String类型的Hql或者Sql语句,去执行。java

这样实际上是很蠢的一种作法!!!!spring

举个栗子~~

咱们模仿一下用户登陆的场景:sql

常见的作法是将前台获取到的用户名和密码,做为字符串动态拼接到查询语句中,而后去调用数据库查询~查询的结果不为null就表明用户存在,则登录成功,不然登陆失败!数据库

正常状况下用户输入帐号是123456和密码123(假设是错误的密码或者说这个用户根本不存在)session

usernameString//前台输入的用户名
passwordString//前台输入的密码
//hql语句
String queryString = "from User t where t.username= " + usernameString + " and  t.password="+ passwordString;
//执行查询
List result = session.createQuery(queryString).list();

正经常使用户输入的话,sql语句被拼接成: from User t where t.username=123456  and t.password=123 ;mybatis

这样是正常的sql语句。能够去查询数据库验证是否有此用户数据。框架

可是!网站

 若是用户在密码输入框中输入:123  or 1=1   做为一个字符串传入后台后加密

sql语句被拼接成: from User t where t.username=123456  and t.password=123 or 1=1;
spa

一旦加上or 1=1 那么这条sql永远成立!!!更严重的能够删除数据库中表,篡改信息,及其严重!!!

咱们来解释一下为何会被SQL注入?

sql注入的缘由,表面上说是由于 拼接字符串,构成sql语句,没有使用 sql语句预编译,绑定变量。

可是更深层次的缘由是,将用户输入的字符串,当成了 “sql语句” 来执行。

好比上面的 String queryString = "from User t where t.username= " + usernameString + " and t.password="+ passwordString;

咱们但愿用户输入的 username和password 的值,仅仅做为一个字符串字面值,传入数据库执行。

可是当输入了:123 or 1=1 时,其中的 or 1=1 并无做为 where id= 的字面值,而是做为了 sql语句 来执行的。因此其本质是将用户的输入的数据,做为了命令来执行。

SQL防护

基本上你们都知道 采用sql语句预编译和绑定变量,是防护sql注入的最佳方法为了防止SQL注入,避免使用拼凑SQL语句的方式!!!

实际项目中,通常咱们都是采用各类的框架,好比ibatis, hibernate,mybatis等等。他们通常也默认就是sql预编译的。对于ibatis/mybatis,若是使用的是 #{name}形式的,那么就是sql预编译,使用 ${name} 就不是sql预编译的。

参数绑定有2种办法:使用positional parameter(查询字符串中使用?)或者named parameter(查询字符串中使用:)

hibernate支持JDBC样式的positional parameter(查询字符串中使用?),它同使用named parameter的效果同样(查询字符串中使用:)。

 

使用named parameter

 

usernameString//前台输入的用户名
passwordString//前台输入的密码
//hql语句
String queryString = "from User t where t.username:usernameString and t.password: passwordString";
//执行查询
List result = session.createQuery(queryString)
                      .setString("usernameString ", usernameString )
                      .setString("passwordString", passwordString)
                      .list();            

使用positional parameter

usernameString//前台输入的用户名
passwordString//前台输入的密码
//hql语句
String queryString = "from User t where t.username=? and t.password=?";
//执行查询
List result = session.createQuery(queryString)
                      .setString(0, usernameString )
                      .setString(1, passwordString)
                      .list();           

二者比较:positional parameter可读性强不如named parameter的强,并且可维护性差,若是咱们的查询稍微改变一点,将第一个参数和第二个参数改变一下位置,

这样咱们的代码中涉及到位置的地方都要修改,因此咱们强烈建议使用named parameter方式进行参数绑定。

最后,在named parameter中可能有一个参数出现屡次的状况,应该怎么处理呢?

在举个栗子~~

咱们模仿一下用户登陆的场景:此次业务变换,有的网站,手机号能够做为用户名来登陆,也能做为手机号自己登陆。

常见的作法是将前台获取到的用户名or手机号和密码,做为字符串动态拼接到查询语句中,而后去调用数据库查询~查询的结果不为null就表明用户存在,则登录成功,不然登陆失败!

正常状况下用户输入帐号是13812345678和密码123

这里usernameString做为手机号又做为用户名出现了两次,怎么办呢?

你们请看下面代码:

usernameString//前台输入的用户名
passwordString//前台输入的密码
//hql语句
String queryString = "from User t where t.username:usernameString and
t.phone:usernameString and t.password: passwordString";
//执行查询
List result = session.createQuery(queryString)
                      .setString("usernameString ", usernameString )
                      .setString("passwordString", passwordString)
                      .list(); 

在Hibernate+spring中getHibernateTemplate()返回的对象能够调用find(String queryString, Object value...Object value)来实现named parameter。好比:

usernameString//前台输入的用户名
passwordString//前台输入的密码
//hql语句
String queryString = "from User t where t.username:usernameString and t.password: passwordString";
//执行查询
return getHibernateTemplate().find(queryString, usernameString, passwordString);     

 PS:其实说这么多都是扯淡,由于如今真是商业项目中,没有把密码以明文的方式存入数据库的,基本上都是通过加密之后进行比对。因此无论用户输入什么都会解密成一个字符串。因此,这种SQL注入基本上已经不存在了~~~~

因此仍是建议你们在开发中,多规范一下本身的代码,让代码更加健壮!

相关文章
相关标签/搜索