ref:https://blog.csdn.net/beidou321/article/details/6482618java
小結:spring採用JdbcTemplate來操做sql,通常不要自行拼接sql,而是使用參數化的構造方式,則不會存在注入問題。程序員
SQL注入每每是在程序員編寫包含用戶輸入的動態數據庫查詢時產生的,但其實防範SQL注入的方法很是簡單。程序員只要spring
a)再也不寫動態查詢b)防止用戶輸入包含可以破壞查詢邏輯的惡意SQL語句,就可以防範SQL注入。sql
在這篇文章中,咱們將會說明一些很是簡單的防止SQL注入的方法。咱們用如下Java代碼做爲示例,數據庫
String query ="SELECT account_balance FROM user_data WHERE user_name =" + request.getParameter("customerName");//sql語句拼接通常都有問題。除+外還有append函數也能夠搜索下。 try { Statement statement = connection.createStatement( …); ResultSet results = Statement.executeQuery(query); }
在以上代碼中,咱們能夠看到並未對變量customerName作驗證,customerName的值能夠直接附在query語句的後面傳送到數據庫執行,則攻擊者能夠將任意的sql語句注入。編程
通常來講,sql注入有兩種:int型注入和字符串注入。對於int注入,只要在拼接時嚴格設置爲int類型,例如intval或者java中函數參數爲int,便可防止注入。api
對於字符串注入,只要對'進行轉義便可。 安全
防範方法1:參數化查詢app
參數化查詢是全部開發人員在作數據庫查詢時首先須要學習的,參數化查詢迫使全部開發者首先要定義好全部的SQL代碼,而後再將每一個參數逐個傳入,這種編碼風格就可以讓數據庫辨明代碼和數據。編程語言
參數化查詢可以確保攻擊者沒法改變查詢的內容,在下面修正過的例子中,若是攻擊者輸入了UsrID是「’or ‘1 ‘=’1」,參數化查詢會去查找一個徹底知足名字爲‘or ‘1 ‘=’ 1的用戶。
對於不一樣編程語言,有一些不一樣的建議:
Java EE——使用帶綁定變量的PreparedStatement();
.Net——使用帶綁定變量的諸如SqlCommand()或OleDbCommand()的參數化查詢;
PHP——使用帶強類型的參數化查詢PDO(使用bindParam());
Hibernate——使用帶綁定變量的createQuery()。
Java示例: String custname = request.getParameter("customerName"); String query ="SELECT account_balance FROM user_data WHERE user_name= ?"; PreparedStatement pstmt =connection.prepareStatement(query); Pstmt.setString(1,custname); ResultSet results = pstmt.executeQuery(); C# .Net示例: String query ="SELECT account_balance FROM user_data WHERE user_name = ?"; Try { OleDbCommand command = new OleDbCommand(query,connection); command.Parameters.Add(new OleDbParameter("customerName",CustomerName.Text)); OleDbDataReader reader = command.ExecuteReader(); }catch (OleDbException se){ //error handling }
防範方法二:存儲過程
存儲過程和參數化查詢的做用是同樣的,惟一的不一樣在於存儲過程是預先定義並存放在數據庫中,從而被應用程序調用的。
Java存儲過程示例: String custname = request.getParameter("customerName"); try { CallableStatement cs = connection.prepareCall("call sp_getAccountBalance(?)}"); cs.setString(1,custname); Result results = cs.executeQuery(); }catch(SQLException se){ //error handling } VB .Net存儲過程示例: Try Dim command As SqlCommand = new SqlCommand("sp_getAccountBalance",connection) command.CommandType = CommandType.StoredProcedure command.Parameters.Add(new SqlParameter("@CustomerName",CustomerName.Text)) Dim reader As SqlDataReader = command.ExecuteReader() ‘… Catch se As SqlException ‘error handling End Try
防範方法三:對全部用戶輸入進行轉義
咱們知道每一個DBMS都有一個字符轉義機制來告知DBMS輸入的是數據而不是代碼,若是咱們將全部用戶的輸入都進行轉義,那麼DBMS就不會混淆數據和代碼,也就不會出現SQL注入了。
固然,若是要採用這種方法,那麼你就須要對所使用的數據庫轉義機制,也可使用現存的諸如OWASP ESAPI的escaping routines。ESAPI目前是基於MySQL和Oracle的轉義機制的,使用起來也很方便。一個Oracle的ESAPI的使用示例以下:
ESAPI.encoder().encodeForSQL(new OracleCodec(),queryparam);
那麼,假設你有一個要訪問Oracle數據庫的動態查詢代碼以下:
String query ="SELECT user_id FROM user_data WHERE user_name = ‘"+req.getParameter("userID")+"’ and user_password = ‘"+req.getParameter("pwd")+"’";
try {
Statement statement = connection.createStatement(…);
ResultSet results = statement.executeQuery(query) ;
}
那麼,你就必須重寫你的動態查詢的第一行以下:
Codec ORACLE_CODEC = new OracleCodec(); String query ="SELECT user_id FROM user_data WHERE user_name = ‘"+//注意必須有引號,若是沒有,則int類型注入就可繞過。 ESAPI.encoder().encodeForSQL(ORACLE_CODEC,req.getParameter("userID"))+"’ and user_password = ‘"+ ESAPI.encoder().encodeForSQL(ORACLE_CODEC,req.getParameter("pwd"))+"’";
固然,爲了保證本身代碼的可讀性,咱們也能夠構建本身的OracleEncoder:
Encoder e = new OracleEncoder();
String query ="SELECT user_id FROM user_data WHERE user_name = ‘"
+ oe.encode(req.getParameter("userID")) +"’ and user_password = ‘"
+ oe.encode(req.getParameter("pwd"))+"’";
除了上面所說的三種防範方法之外,咱們還建議能夠用如下兩種附加的方法來防範SQL注入:最小權限法、輸入驗證白名單法。
最小權限法:
爲了不注入攻擊對數據庫形成的損害,咱們能夠把每一個數據庫用戶的權限盡量縮小,不要把DBA或管理員的權限賦予你應用程序帳戶,在給用戶權限時是基於用戶須要什麼樣的權限,而不是用戶不須要什麼樣的權限。當一個用戶只須要讀的權限時,咱們就只給他讀的權限,當用戶只須要一張表的部分數據時,咱們寧願另建一個視圖讓他訪問。若是你的策略是都是用存儲過程的話,那麼僅容許應用程序的帳戶執行這些查詢,而不給他們直接訪問數據庫表的權限。諸如此類的最小權限法可以在很大程度上保證咱們數據庫的安全。
輸入驗證白名單法:
輸入驗證可以在數據傳遞到SQL查詢前就察覺到輸入是否正確合法,採用白名單而不是黑名單則能在更大程度上保證數據的合法性。