1. 程式人生 > 資料庫 >SQL注入攻擊,嚇死寶寶了

SQL注入攻擊,嚇死寶寶了

SQL注入攻擊是黑客對資料庫進行攻擊的常用手段之一。那麼,什麼是SQL注入?SQL注入是怎麼發生的?如何防止SQL注入。我們今天就來了解一下。

1、SQL注入產生的背景
在企業級應用中,儲存資料主要是使用資料庫來實現。資料庫有著強大的資料儲存能力和資料處理效能。

對於開發者而言,完成資料庫的資料儲存和資料處理,主要是利用SQL語句來實現的。不過,SQL語句中的資料是由使用者在客戶端通過文字框進行資料錄入,然後,通過表單提交的方式請求伺服器。開發者為了將客戶端提交的資料和資料庫進行資料的互動。就需要將客戶端提交的資料和SQL語句的進行拼接,形成一條完整的能夠執行的SQL語句。然後,交給DBMS執行,完成資料的儲存以及資料處理。

舉個新增的例子,SQL語句應該書寫如下:

insert into t_user(userName,sex) values('tom','男');

這條SQL語句執行完成後,可以向資料庫新增使用者名稱為"tom",性別為"男"的記錄。但是,在實際應用中,使用者名稱和性別應該是由客戶端提供的。這時,開發者應該先得到從客戶端提交資料,然後拼接成新增SQL語句,交由DBMS執行。

String name = request.getParameter("userName");
String sex = request.getParameter("sex");

String sql = "insert into t_user(userName,sex) values('"+name+"','"+sex+"')";

這時,如果客戶端提交的userName值為"john",性別為"男"。那麼拼接後的SQL語句就應該是:

String sql = "insert into t_user(userName,sex) values('john','男')";

這樣,DBMS在執行SQL語句後,就會將"john"和"男"新增進資料庫。

不過,在這個過程中,存在一些漏洞可以被黑客所攻擊。如果客戶端提交的資料存在一些非法字元或資料庫關鍵字時,可能會造成SQL語句發生錯誤,或執行結果不正確的情況。

在上面的示例中,如果客戶端提交的userName值為"jo'hn"。性別為"男"。那麼拼接後的SQL語句就應該是:

String sql = "insert into t_user(userName,sex) values('jo'hn','男')";

很明顯,這條SQL語句不滿足新增語句的語法。DBMS在執行時會報SQL語句語法錯誤。

再來看一個登陸的例子,sql語句應該書寫如下:

String name = request.getParameter("userName");
String password = request.getParameter("password");

String sql = "select * from t_user where userName='"+name+"' and password='"+password+"'";

這條SQL語句的本意是,查詢使用者名稱和密碼同時相等的記錄。如果客戶端提交的userName值為"tom",密碼為"123"。那麼拼接後的SQL語句就應該是:

String sql = "select * from t_user where userName='tom' and password='123'";

但是,如果客戶端提交的userName值為"aaa"。密碼為"bbb' or '1'='1"。那麼拼接後的SQL語句就應該是:

String sql = "select * from t_user where userName='aaa' and password='bbb' or '1'='1'";

由於'1'='1'固定為true,所以,這條SQL語句的查詢結果,是t_user表中所有記錄。這樣的話,無論使用者名稱和密碼輸什麼值,都可以有結果返回。從而,開發者會得到"登陸成功"的錯誤結論。

有些黑客也利用,在資料中加入"--"的方式,註釋掉SQL語句中的條件。比如,客戶端提交的userName值為"aaa' or 1=1 -- "。密碼為"bbb"。那麼拼接後的SQL語句就應該是:

String sql = "select * from t_user where userName='aaa' or 1=1 -- ' and password='bbb'";

這樣,1=1固定為true,而以後的SQL語句會因為註釋不會執行。

如果黑客再壞一點,在資料中加入DDL操作,比如:在密碼中填入"bbb';drop table t_user;",那麼SQL語句就變成:

String sql = "select * from t_user where userName='aaa' and password='bbb';drop table t_user";

執行SQL語句後,表都沒了。


2、什麼是SQL注入?
所謂SQL注入,是將客戶機提交或Web表單遞交的資料,拼接成SQL語句字串時。如果客戶端提交的資料有非法字元或SQL語句關鍵字時,會造成執行的SQL語句語法錯誤,或執行結果不正確的情況。通過SQL注入,黑客可以最終達到欺騙伺服器,執行惡意的SQL語句,甚至破壞資料庫結構的目的。

sql注入攻擊大多是利用設計上的漏洞,在目標伺服器上執行Sql語句的攻擊方式。開發者在動態生成Sql語句時,沒有對使用者輸入的資料進行驗證,是Sql注入攻擊得逞的主要原因。

3、如何防止SQL注入?
在java中,是使用JDBC和資料庫建立連線,並執行SQL語句,和資料庫進行資料互動的。

JDBC在執行SQL語句操作時,提供了Statement、PreparedStatement和CallableStatement三種方式來執行SQL語句。其中 Statement 用於通用查詢, PreparedStatement 用於執行引數化查詢,而 CallableStatement則是用於儲存過程。

在三個介面中,Statement是PreparedStatement和CallableStatement的父介面。Statement在執行SQL語句時,對於客戶端提交的資料只支援拼接SQL語句的方式。

ResultSet rs = statemnt.executeQuery("select * from t_user where userName='"+name+"' and password='"+password+"'")

所以,使用Statement在執行SQL語句,容易引起SQL注入。

PreparedStatement在執行引數化查詢時,支援佔位符方式。

PreparedStatement ps = conn.prepareStatement("select * from t_user where userName=? and password=?");
ps.setString(1,name);
ps.setString(2,password);

在使用引數化查詢的情況下,資料庫系統不會將引數的內容,視為SQL指令的一部分來處理。而是在資料庫完成SQL指令的編譯後,才套用引數執行。因此,就算引數中含有破壞性的指令,也不會被資料庫所執行。所以,使用PreparedStatement的引數化查詢可以有效的阻止SQL注入。

另外,PreparedStatement相比Statement還有以下幾個優勢
1、可以預編譯SQL語句,多次查詢時速度快。
2、防止資料庫緩衝區溢位
3、程式碼的可讀性可維護性好

由於有以上優點,所以,在開發JDBC時,PreparedStatement成為訪問資料庫的語句物件的首選。

總結:
1、所謂SQL注入,是將客戶機提交或Web表單遞交的資料,拼接成SQL語句字串時。如果客戶端提交的資料有非法字元或SQL語句關鍵字時,會造成執行的SQL語句語法錯誤,或執行結果不正確的情況。通過SQL注入,黑客可以最終達到欺騙伺服器,執行惡意的SQL語句,甚至破壞資料庫結構的目的。

2、在JDBC中使用PreparedStatement的引數化查詢,資料庫系統不會將引數的內容,視為SQL指令的一部分來處理。可以有效防止SQL注入。

3、開發JDBC時,儘量採用 PreparedStatement執行SQL語句,相比Statement有以下優勢
a、可以防止SQL注入
b、可以預編譯SQL語句,多次查詢時速度快
c、防止資料庫緩衝區溢位
d、程式碼的可讀性可維護性好