1、什么是Sql注入攻擊
SQL注入攻擊通過構建特殊的輸入作為參數傳入Web應用程序,而這些輸入大都是SQL語法里的一些組合,通過執行SQL語句進而執行攻擊者所要的操作,它目前是黑客對數據庫進行攻擊的最常用手段之一。
本文將帶你從介紹 Web 應用運行原理開始,一步一步理解 Sql 注入的由來、原理和攻擊方式。
2、Web程序三層架構
三層架構(3-tier architecture) 通常意義上就是將整個業務應用劃分為:
界面層(User Interface layer)
業務邏輯層(Business Logic Layer)
數據訪問層(Data access layer)。
區分層次的目的即為了“高內聚低耦合”的思想。在軟件體系架構設計中,分層式結構是最常見,也是最重要的一種結構被應用于眾多類型的軟件開發。
3、Sql注入產生原因及威脅
剛剛講過當我們訪問動態網頁時, Web 服務器會向數據訪問層發起 Sql 查詢請求,如果權限驗證通過就會執行 Sql 語句。 這種網站內部直接發送的Sql請求一般不會有危險,但實際情況是很多時候需要結合用戶的輸入數據動態構造 Sql 語句,如果用戶輸入的數據被構造成惡意 Sql 代碼,Web 應用又未對動態構造的 Sql 語句使用的參數進行審查,則會帶來意想不到的危險。
Sql 注入帶來的威脅主要有如下幾點
猜解后臺數據庫,這是利用最多的方式,盜取網站的敏感信息。
繞過認證,列如繞過驗證登錄網站后臺。
注入可以借助數據庫的存儲過程進行提權等操作
4、判斷Sql注入點
4.1 判斷是否存在sql注入漏洞
通常情況下,可能存在 Sql 注入漏洞的 Url 是類似這種形式 :http://xxx.xxx.xxx/abcd.php?id=XX對 Sql 注入的判斷,主要有兩個方面:
判斷該帶參數的 Url 是否存在 Sql 注入?
如果存在 Sql 注入,那么屬于哪種 Sql 注入?
可能存在 Sql 注入攻擊的 ASP/PHP/JSP 動態網頁中,一個動態網頁中可能只有一個參數,有時可能有多個參數。有時是整型參數,有時是字符串型參數,不能一概而論。總之只要是帶有參數的 動態網頁且此網頁訪問了數據庫,那么就有可能存在 Sql 注入。如果程序員沒有足夠的安全意識,沒有進行必要的字符過濾,存在SQL注入的可能性就非常大。
最為經典的單引號判斷法: 在參數后面加上單引號,比如:
1 http://xxx/abc.php?id=1'
如果頁面返回錯誤,則存在 Sql 注入。 原因是無論字符型還是整型都會因為單引號個數不匹配而報錯。 (如果未報錯,不代表不存在 Sql 注入,因為有可能頁面對單引號做了過濾,這時可以使用判斷語句進行注入,因為此為入門基礎課程,就不做深入講解了)
4.2 判斷Sql注入漏洞的類型
通常 Sql 注入漏洞分為 2 種類型:
數字型
字符型
其實所有的類型都是根據數據庫本身表的類型所產生的,在我們創建表的時候會發現其后總有個數據類型的限制,而不同的數據庫又有不同的數據類型,但是無論怎么分常用的查詢數據類型總是以數字與字符來區分的,所以就會產生注入點為何種類型。
4.2.1 數字型判斷
當輸入的參 x 為整型時,通常 abc.php 中 Sql 語句類型大致如下: select * from <表名> where id = x 這種類型可以使用經典的 and 1=1 和 and 1=2 來判斷:
Url 地址中輸入 http://xxx/abc.php?id= x and 1=1 頁面依舊運行正常,繼續進行下一步。
Url 地址中繼續輸入 http://xxx/abc.php?id= x and 1=2 頁面運行錯誤,則說明此 Sql 注入為數字型注入。
原因如下: 當輸入 and 1=1時,后臺執行 Sql 語句:
1 select * from <表名> where id = x and 1=1
沒有語法錯誤且邏輯判斷為正確,所以返回正常。
當輸入 and 1=2時,后臺執行 Sql 語句:
1 select * from <表名> where id = x and 1=2
沒有語法錯誤但是邏輯判斷為假,所以返回錯誤。 我們再使用假設法:如果這是字符型注入的話,我們輸入以上語句之后應該出現如下情況:
1 2 select * from <表名> where id = 'x and 1=1' select * from <表名> where id = 'x and 1=2'
查詢語句將 and 語句全部轉換為了字符串,并沒有進行 and 的邏輯判斷,所以不會出現以上結果,故假設是不成立的。
4.2.2 字符型判斷
當輸入的參 x 為字符型時,通常 abc.php 中 SQL 語句類型大致如下: select * from <表名> where id = ‘x’ 這種類型我們同樣可以使用 and ‘1’=’1 和 and ‘1’=’2來判斷:
Url 地址中輸入 http://xxx/abc.php?id= x’ and ‘1’=’1 頁面運行正常,繼續進行下一步。
Url 地址中繼續輸入 http://xxx/abc.php?id= x’ and ‘1’=’2 頁面運行錯誤,則說明此 Sql 注入為字符型注入。
原因如下: 當輸入 and ‘1’=’1時,后臺執行 Sql 語句:
1 select * from <表名> where id = 'x' and '1'='1'
語法正確,邏輯判斷正確,所以返回正確。
當輸入 and ‘1’=’2時,后臺執行 Sql 語句:
1 select * from <表名> where id = 'x' and '1'='2'
語法正確,但邏輯判斷錯誤,所以返回正確。同學們同樣可以使用假設法來驗證。
5、Sql注入實例
5.1 繞過登陸
我們正常的登陸是輸入用戶名密碼到后臺數據庫判斷用戶名密碼是否正確,看看后臺代碼是如何實現的
1 select * from user where username = '$name' and password = '$pwd'"
用戶名密碼都輸入123,實際執行的sql語句是:
1 select * from users where username='123' and password='123'
我們嘗試在用戶名中輸入 123’ or 1=1 #, 密碼同樣輸入 123’ or 1=1 #顯示登陸成功,為什么會登錄成功呢,我們來看看實際執行的sql語句
1 select * from users where username='123' or 1=1 #' and password='123' or 1=1 #'
按照 MySQL 語法,# 后面的內容會被忽略,所以以上語句等同于(實際上密碼框里不輸入任何東西也一樣):
1 select * from users where username='123' or 1=1
由于判斷語句 or 1=1 恒成立,所以結果當然返回真,成功登錄。 我們再嘗試不使用 # 屏蔽單引號,采用手動閉合的方式: 我們嘗試在用戶名中輸入 123’ or ‘1’=’1, 密碼同樣輸入 123’ or ‘1’=’1 (不能少了單引號,否則會有語法錯誤): 此處輸入圖片的描述 此處輸入圖片的描述 實際執行的 Sql 語句是:
1 select * from users where username='123' or '1'='1' and password='123' or '1'='1`
看到了嗎?兩個 or 語句使 and 前后兩個判斷永遠恒等于真,所以能夠成功登錄。
還有通過sql語句注入來獲取隱私信息,所以sql注入需要引起程序員們的注意。
6、預防Sql注入的方法
下面我針對JSP,說一下應對方法:
(1)(簡單又有效的方法)PreparedStatement
采用預編譯語句集,它內置了處理SQL注入的能力,只要使用它的setXXX方法傳值即可。
使用好處:
(1).代碼的可讀性和可維護性.
(2).PreparedStatement盡最大可能提高性能.
(3).最重要的一點是極大地提高了安全性.
原理:
sql注入只對sql語句的準備(編譯)過程有破壞作用
而PreparedStatement已經準備好了,執行階段只是把輸入串作為數據處理,
而不再對sql語句進行解析,準備,因此也就避免了sql注入問題.
(2) 使用正則表達式過濾傳入的參數
要引入的包:
import JAVA.util.regex.*;
正則表達式:
private String CHECKSQL = “^(.+)sands(.+)|(.+)sor(.+)s$”;
判斷是否匹配:
Pattern.matches(CHECKSQL,targerStr);
下面是具體的正則表達式:
檢測SQL meta-characters的正則表達式 :
/(%27)|(’)|(–)|(%23)|(#)/ix
修正檢測SQL meta-characters的正則表達式 :/((%3D)|(=))[^n]*((%27)|(’)|(–)|(%3B)|(:))/i
典型的SQL 注入攻擊的正則表達式 :/w*((%27)|(’))((%6F)|o|(%4F))((%72)|r|(%52))/ix
檢測SQL注入,UNION查詢關鍵字的正則表達式 :/((%27)|(’))union/ix(%27)|(’)
檢測MS SQL Server SQL注入攻擊的正則表達式:
/exec(s|+)+(s|x)pw+/ix
等等…..
(3) 字符串過濾
比較通用的一個方法:
(||之間的參數可以根據自己程序的需要添加)
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[] = split(inj_str,”|”); for (int i=0 ; i < inj_stra.length ; i++ ) { if (str.indexOf(inj_stra[i])>=0) { return true; } } return false; }
(4) jsp中調用該函數檢查是否包函非法字符
防止SQL從URL注入:
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; } }
(5) JSP頁面判斷代碼:
使用JavaScript在客戶端進行不安全字符屏蔽
功能介紹:檢查是否含有”‘”,””,”/”
參數說明:要檢查的字符串
返回值:0:是1:不是
函數名是
function check(a) { return 1; fibdn = new Array (”‘” ,””,”/”); i=fibdn.length; j=a.length; for (ii=0; ii<i; ii++) { for (jj=0; jj<j; jj++) { temp1=a.charAt(jj); temp2=fibdn[ii]; if (tem’; p1==temp2) { return 0; } } } return 1; }
總的說來,防范一般的SQL注入只要在代碼規范上下點功夫就可以了。
凡涉及到執行的SQL中有變量時,用JDBC(或者其他數據持久層)提供的如:PreparedStatement就可以 ,切記不要用拼接字符串的方法就可以了。
看完本篇文章,小伙伴們明白sql注入的基本原理了嗎?