之前寫程式碼,往後台傳入一個組織好的String類型的Hql或Sql語句,去執行。
這樣其實是很蠢的做法! ! ! !
我們模仿使用者登入的場景:
常見的做法是將前台取得的用戶名稱和密碼,作為字串動態拼接到查詢語句中,然後去呼叫資料庫查詢~查詢的結果不為null就代表使用者存在,則登陸成功,否則登入失敗!
正常情況下使用者輸入帳號是123456和密碼123(假設是錯誤的密碼或說這個使用者根本不存在)
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 ;
這樣是正常的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語句” 來執行。
例如上面的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語句的方式! ! !
在實際專案中,一般我們都是採用各種的框架,像是ibatis, hibernate,mybatis等等。他們一般也預設就是sql預編譯的。對於ibatis/mybatis,如果使用的是 #{name}形式的,那就是sql預先編譯,使用 ${name} 就不是sql預先編譯的。
使用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方式进行参数绑定。
在举个栗子~~
我们模仿一下用户登录的场景:这次业务变换,有的网站,手机号可以作为用户名来登录,也能作为手机号本身登录。
常见的做法是将前台获取到的用户名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);
以上是怎樣利用Hibernate來防止SQL注入的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!