之前寫代碼,往後臺傳入一個組織好的String類型的Hql或者Sql語句,去執行。 這樣其實是很蠢的一種做法!!!! 舉個慄子~~ 我們模仿一下用戶登錄的場景: 常見的做法是將前臺獲取到的用戶名和密碼,作為字元串動態拼接到查詢語句中,然後去調用資料庫查詢~查詢的結果不為null就代表用戶存在,則登陸 ...
之前寫代碼,往後臺傳入一個組織好的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語句預編譯,綁定變數。
但是更深層次的原因是,將用戶輸入的字元串,當成了 “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);