傳參數前rankid=URLEncoder.encode(rankid, "UTF-8 ");/*把漢字變成UTF-8編碼*/
然後在取參數時候在rankid=URLDecoder.decoder(rankid, "UTF-8 ");
防SQL注入:
在JDBC應用中,如果你已經是稍有水平開發人員,你就應該始終以PreparedStatement代替Statement.也就是說,在任何時候都不要使用Statement.
基於以下的原因:
一.代碼的可讀性和可維護性.
雖然用PreparedStatement來代替Statement會使代碼多出幾行,但這樣的代碼無論從可讀性還是可維護性上來說.都比直接用Statement的代碼高很多檔次:
代碼如下 |
複製代碼 |
stmt.executeUpdate( "insert into tb_name (col1,col2,col2,col4) values ( ' "+var1+ " ', ' "+var2+ " ', "+var3+ ", ' "+var4+ " ') "); perstmt = con.prepareStatement( "insert into tb_name (col1,col2,col2,col4) values (?,?,?,?) "); perstmt.setString(1,var1); perstmt.setString(2,var2); perstmt.setString(3,var3); perstmt.setString(4,var4); perstmt.executeUpdate(); |
不用我多說,對於第一種方法.別說其他人去讀你的代碼,就是你自己過一段時間再去讀,都會覺得傷心.
二.PreparedStatement盡最大可能提高效能.
每一種資料庫都會盡最大努力對先行編譯語句提供最大的效能最佳化.因為先行編譯語句有可能被重複調用.所以語句在被DB的編譯器編譯後的執行代碼被緩衝下來,那麼下次調用時只要是相同的先行編譯語句就不需要編譯,只要將參數直接傳入編譯過的語句執行代碼中(相當於一個涵數)就會得到執行.這並不是說只有一個 Connection中多次執行的先行編譯語句被緩衝,而是對於整個DB中,只要先行編譯的語句文法和緩衝中匹配.那麼在任何時候就可以不需要再次編譯而可以直接執行.而statement的語句中,即使是相同一操作,而由於每次操作的資料不同所以使整個語句相匹配的機會極小,幾乎不太可能匹配.比如:
代碼如下 |
複製代碼 |
insert into tb_name (col1,col2) values ( '11 ', '22 '); insert into tb_name (col1,col2) values ( '11 ', '23 ');
|
即使是相同操作但因為資料內容不一樣,所以整個個語句本身不能匹配,沒有緩衝語句的意義.事實是沒有資料庫會對普通語句編譯後的執行代碼緩衝.這樣每執行一次都要對傳入的語句編譯一次.
當然並不是所以先行編譯語句都一定會被緩衝,資料庫本身會用一種策略,比如使用頻度等因素來決定什麼時候不再緩衝已有的先行編譯結果.以儲存有更多的空間儲存新的先行編譯語句.
三.最重要的一點是極大地提高了安全性.
即使到目前為止,仍有一些人連基本的惡義SQL文法都不知道.
代碼如下 |
複製代碼 |
String sql = "select * from tb_name where name= ' "+varname+ " ' and passwd= ' "+varpasswd+ " ' ";
|
如果我們把[ ' or '1 ' = '1]作為varpasswd傳入進來.使用者名稱隨意,看看會成為什麼?
代碼如下 |
複製代碼 |
select * from tb_name = '隨意 ' and passwd = ' ' or '1 ' = '1 ';
|
因為 '1 '= '1 '肯定成立,所以可以任何通過驗證.更有甚者:
把[ ';drop table tb_name;]作為varpasswd傳入進來,則:
select * from tb_name = '隨意 ' and passwd = ' ';drop table tb_name;有些資料庫是不會讓你成功的,但也有很多資料庫就可以使這些語句得到執行.
而如果你使用先行編譯語句.你傳入的任何內容就不會和原來的語句發生任何匹配的關係.(前提是資料庫本身支援先行編譯,但上前可能沒有什麼服務端資料庫不支援編譯了,只有少數的案頭資料庫,就是直接檔案訪問的那些)只要全使用先行編譯語句,你就用不著對傳入的資料做任何過慮.而如果使用普通的statement, 有可能要對drop,;等做費盡心機的判斷和過慮.
sql_inj.java為一個改進的防注入bean,編譯後將class檔案放在tomcat的classes下的sql_inj目錄中。
sql_inj.java代碼:
====================================================================
代碼如下 |
複製代碼 |
package sql_inj; import java.net.*; import java.io.*; import java.sql.*; import java.text.*; import java.lang.String; public class sql_inj{ public static boolean sql_inj(String str) { String inj_str = "'|and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+|,";//這裡的東西還可以自己添加 String[] inj_stra=inj_str.split("\|"); for (int i=0 ; i < inj_stra.length ; i++ ) { if (str.indexOf(inj_stra[i])>=0) { return true; } } return false; } } |
====================================================================
JSP頁面判斷代碼:
====================================================================
代碼如下 |
複製代碼 |
<jsp:useBean id="sql_inj" class="sql_inj.sql_inj" scope="page"/> <% String currenturl = request.getRequestURI()+(request.getQueryString()==null?"":("?"+request.getQueryString())); if (sql_inj.sql_inj(currenturl)){ //判斷url及參數中是否包含注入代碼,是的話就跳轉到某頁。 response.sendRedirect("/"); return; } //out.println(currenturl); %>
|