1.關於SQL注入
什麼是SQL注入:
首先看一下以下代碼:
String sql = "select* from users where username='" + userName
+ "' and password='" + passWord+"'";
Connection conn = null;
Statement state = null;
ResultSet result;
conn = JdbcUtil.getConnection();
System.out.println(sql);
try {
state = conn.createStatement();
result = state.executeQuery(sql);
這是一段根據傳入用戶名,密碼查找用戶表的代碼。
在做用戶登錄的驗證的時候,我們可能會根據用戶所填寫的用戶名和密碼在後臺拼成一條SQL語句執行,去查用戶表:
select* from users where username='張三' and password='小張',如果能查出結果則表示驗證成功,允許登錄,否則賬號或密碼錯誤不允許登錄。那麼在組成這條語句的過程中會存在一個叫做SQL注入的問題,就是用戶在輸入用戶名或密碼的時候填寫某些內容使得後臺所拼成的SQL語句語義有所變化。
舉個例子,在沒有防止SQL的情況下:假如我們知道一個用戶叫做張三,但是不知道這個用戶的密碼是什麼,我們依然可以在登錄的時候在用戶輸入框寫上:張三'# 然後密碼框任意填:njksad。一點擊登錄,會發現居然能夠登錄上去。那是爲什麼呢?
這是因爲#在SQL中的意思是註釋,那麼我們根據上面的情況來分析一下最終所拼成的SQL語句是怎樣的,
select* from users where username='張三'#' and password='njksad'
爲了讓大家能夠看清楚上面那條SQL語句,筆者特地加大顯示,可以看到 username='張三' 之後是一個#
那就意味着之後的內容都是註釋,也就是可以忽略掉那麼這條語句真正發揮作用的部分就是:select* from users where username='張三'
直接變成了一條查找張三 的語句,完全不用經過密碼驗證。
2.防止SQL注入攻擊
那麼怎麼才能做到防止SQL注入攻擊呢?
在上面那段代碼中,Statement的對象是用來執行SQL語句的,Statement有一個子類叫做PreparedStatement,可以做到防止SQL注入攻擊,接下來我們來看看PreparedStatement有什麼特點以及怎麼使用:
PreparedStatement是Statement的孩子,不同的是,PreparedStatement使用預編譯機制,在創建PreparedStatement對象時就需要將sql語句傳入,傳入的過程中參數要用?替代,這個過程回導致傳入的sql被進行預編譯,然後再調用PreparedStatement的setXXX將參數設置上去,由於sql語句已經經過了預編譯,再傳入特殊值也不會起作用了。
而且PreparedStatement使用了預編譯機制,sql語句在執行的過程中效率比Statement要高。
String sql = "select* from users where username=? and password=?";
Connection conn = null;
PreparedStatement state = null;
ResultSet result;
conn = JdbcUtil.getConnection();
System.out.println(sql);
try {
state = conn.prepareStatement(sql);
state.setString(1, userName);
state.setString(2, passWord);
result = state.executeQuery();