使⽤Hibernate防⽌SQL注⼊的⽅法
之前写代码,往后台传⼊⼀个组织好的String类型的Hql或者Sql语句,去执⾏。
这样其实是很蠢的⼀种做法
举个栗⼦~~
我们模仿⼀下⽤户登录的场景:
常见的做法是将前台获取到的⽤户名和密码,作为字符串动态拼接到查询语句中,然后去调⽤数据库查询~查询的结果不为null就代表⽤户存在,则登陆成功,否则登录失败!
正常情况下⽤户输⼊账号是123456和密码123(假设是错误的密码或者说这个⽤户根本不存在)
usernameString//前台输⼊的⽤户名
passwordString//前台输⼊的密码
//hql语句
String queryString = "from User t where t.username= " + usernameString + " and t.password="+ passwordString;
//执⾏查询
List result = ateQuery(queryString).list();
正常⽤户输⼊的话,sql语句被拼接成: from User t where t.username=123456 and t.password=123 ;
这样是正常的sql语句。可以去查询数据库验证是否有此⽤户数据。
但是!
如果⽤户在密码输⼊框中输⼊:123 or 1=1 作为⼀个字符串传⼊后台后
sql语句被拼接成: from User t where t.username=123456 and t.password=123 or 1=1;
⼀旦加上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(查询字符串中使⽤:)。
⽀持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 = ateQuery(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 = ateQuery(queryString)
.setString(0, usernameString )
.setString(1, passwordString)
.list();
密码字符串是什么两者⽐较:positional parameter可读性强不如named parameter的强,⽽且可维护性差,如果我们的查询稍微改变⼀点,将第⼀个参数和第⼆个参数改变⼀下位置,
这样我们的代码中涉及到位置的地⽅都要修改,所以我们强烈建议使⽤named parameter⽅式进⾏参数绑定。
最后,在named parameter中可能有⼀个参数出现多次的情况,应该怎么处理呢?
在举个栗⼦~~
我们模仿⼀下⽤户登录的场景:这次业务变换,有的⽹站,⼿机号可以作为⽤户名来登录,也能作为⼿机号本⾝登录。
常见的做法是将前台获取到的⽤户名or⼿机号和密码,作为字符串动态拼接到查询语句中,然后去调⽤数据库查询~查询的结果不为null就代表⽤户存在,则登陆成功,否则登录失败!
正常情况下⽤户输⼊账号是138********和密码123
这⾥usernameString作为⼿机号⼜作为⽤户名出现了两次,怎么办呢?
⼤家请看下⾯代码:
usernameString//前台输⼊的⽤户名
passwordString//前台输⼊的密码
//hql语句
String queryString = "from User t where t.username:usernameString and
t.phone:usernameString and t.password: passwordString";
//执⾏查询
List result = ateQuery(queryString)
.setString("usernameString ", usernameString )
.setString("passwordString", passwordString)
.list();
在Hibernate+中getHibernateTemplate()返回的对象可以调⽤find(String queryString, 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注⼊基本上已经不存在了~~~~
所以还是建议⼤家在开发中,多规范⼀下⾃⼰的代码,让代码更加健壮!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论