SQL注入原理深度解析

【51CTO.com 獨家特稿】對於Web應用來說,注射式***由來已久,***方式也五花八門,常見的***方式有SQL注射、命令注射以及新近纔出現的XPath注射等等。本文將以SQL注射爲例,在源碼級對其***原理進行深入的講解。
一、注射式***的原理
注射式***的根源在於,程序命令和用戶數據(即用戶輸入)之間沒有做到涇渭分明。這使得***者有機會將程序命令當作用戶輸入的數據提交給We程序,以發號施令,爲所欲爲。
爲了發動注射***,***者需要在常規輸入中混入將被解釋爲命令的“數據”,要想成功,必須要做三件事情:
1.確定Web應用程序所使用的技術
注射式***對程序設計語言或者硬件關係密切,但是這些可以通過適當的踩點或者索性將所有常見的注射式***都搬出來逐個試一下就知道了。爲了確定所採用的技術,***者可以考察Web頁面的頁腳,查看錯誤頁面,檢查頁面源代碼,或者使用諸如Nessus等工具來進行刺探。
2.確定所有可能的輸入方式
Web應用的用戶輸入方式比較多,其中一些用戶輸入方式是很明顯的,如HTML表單;另外,***者可以通過隱藏的HTML表單輸入、HTTP頭部、cookies、甚至對用戶不可見的後端AJAX請求來跟Web應用進行交互。一般來說,所有HTTP的GET和POST都應當作用戶輸入。爲了找出一個Web應用所有可能的用戶輸入,我們可以求助於Web代理,如Burp等。
3.查找可以用於注射的用戶輸入
在找出所有用戶輸入方式後,就要對這些輸入方式進行篩選,找出其中可以注入命令的那些輸入方式。這個任務好像有點難,但是這裏有一個小竅門,那就是多多留意Web應用的錯誤頁面,很多時候您能從這裏得到意想不到的收穫。
二、SQL注射原理
上面對注射***做了一般性的解釋,下面我們以SQL注射爲例進行講解,以使讀者對注射***有一個感性的認識,至於其他***,原理是一致的。
SQL注射能使***者繞過認證機制,完全控制遠程服務器上的數據庫。SQL是結構化查詢語言的簡稱,它是訪問數據庫的事實標準。目前,大多數Web應用都使用SQL數據庫來存放應用程序的數據。幾乎所有的Web應用在後臺都使用某種SQL數據庫。跟大多數語言一樣,SQL語法允許數據庫命令和用戶數據混雜在一起的。如果開發人員不細心的話,用戶數據就有可能被解釋成命令,這樣的話,遠程用戶就不僅能向Web應用輸入數據,而且還可以在數據庫上執行任意命令了。
三、繞過用戶認證
我們這裏以一個需要用戶身份認證的簡單的Web應用程序爲例進行講解。假定這個應用程序提供一個登錄頁面,要求用戶輸入用戶名和口令。用戶通過HTTP請求發送他們的用戶名和口令,之後,Web應用程序檢查用戶傳遞來用戶名和口令跟數據庫中的用戶名和口令是否匹配。這種情況下,會要求在SQL數據庫中使用一個數據庫表。開發人員可以通過以下SQL語句來創建表:
CREATETABLEuser_table(
idINTEGERPRIMARYKEY,
usernameVARCHAR(32),
passwordVARCHAR(41)
);
上面的SQL代碼將建立一個表,該表由三欄組成。第一欄存放的是用戶ID,如果某人經過認證,則用此標識該用戶。第二欄存放的是用戶名,該用戶名最多由32字符組成。第三欄存放的是口令,它由用戶的口令的hash值組成,因爲以明文的形式來存放用戶的口令實在太危險,所以通常取口令的散列值進行存放。我們將使用SQL函數PASSWORD()來獲得口令的hash值,在MySQL中,函數PASSWORD()的輸出由41字符組成。
對一個用戶進行認證,實際上就是將用戶的輸入即用戶名和口令跟表中的各行進行比較,如果跟某行中的用戶名和口令跟用戶的輸入完全匹配,那麼該用戶就會通過認證,並得到該行中的ID。假如用戶提供的用戶名和口令分別爲lonelynerd15和mypassword,那麼檢查用戶ID過程如下所示:
SELECTidFROMuser_tableWHEREusername='lonelynerd15'ANDpassword=PASSWORD('mypassword')
如果該用戶位於數據庫的表中,這個SQL命令將返回該用戶相應的ID,這就意味着該用戶通過了認證;否則,這個SQL命令的返回爲空,這意味着該用戶沒有通過認證。
下面是用來實現自動登錄的Java代碼,它從用戶那裏接收用戶名和口令,然後通過一個SQL查詢對用戶進行認證:
Stringusername=req.getParameter("username");
Stringpassword=req.getParameter("password");
Stringquery="SELECTidFROMuser_tableWHERE"+
"username='"+username+"'AND"+
"password=PASSWORD('"+password+"')";
ResultSetrs=stmt.executeQuery(query);
intid=-1;//-1impliesthattheuserisunauthenticated.
while(rs.next()){
id=rs.getInt("id");
}
開頭兩行代碼從HTTP請求中取得用戶輸入,然後在下一行開始構造一個SQL查詢。執行查詢,然後在while()循環中得到結果,如果一個用戶名和口令對匹配,就會返回正確的ID。否則,id的值仍然爲-1,這意味着用戶沒有通過認證。表面上看,如果用戶名和口令對匹配,那麼該用戶通過認證;否則,該用戶不會通過認證——但是,事實果真如此嗎?非也!讀者也許已經注意到了,這裏並沒有對SQL命令進行設防,所以***者完全能夠在用戶名或者口令字段中注入SQL語句,從而改變SQL查詢。爲此,我們仔細研究一下上面的SQL查詢字符串:
Stringquery="SELECTidFROMuser_tableWHERE"+
"username='"+username+"'AND"+
"password=PASSWORD('"+password+"')";
上述代碼認爲字符串username和password都是數據,不過,***者卻可以隨心所欲地輸入任何字符。如果一位***者輸入的用戶名爲
’OR1=1—
而口令爲
x
那麼查詢字符串將變成下面的樣子:
SELECTidFROMuser_tableWHEREusername=''OR1=1--'ANDpassword
=PASSWORD('x')
該雙劃符號--告訴SQL解析器,右邊的東西全部是註釋,所以不必理會。這樣,查詢字符串相當於:
SELECTidFROMuser_tableWHEREusername=''OR1=1
如今的SELECT語句跟以前的已經大相徑庭了,因爲現在只要用戶名爲長度爲零的字符串''或1=1這兩個條件中一個爲真,就返回用戶標識符ID——我們知道,1=1是恆爲真的。所以這個語句將返回user_table中的所有ID。在此種情況下,***者在username字段放入的是SQL指令'OR1=1--而非數據。
四、構造SQL注射代碼
爲了成功地注入SQL命令,***者必須將開發人員的現有SQL命令轉換成一個合法的SQL語句,當然,要盲注是有些難度的,但一般都是這樣:
'OR1=1–
或者
')OR1=1--
此外,許多Web應用提供了帶來錯誤報告和調試信息,例如,利用'OR1=1--對Web應用進行盲注時,經常看到如下所示的錯誤信息:
Errorexecutingquery:
YouhaveanerrorinyourSQLsyntax;
checkthemanualthatcorrespondstoyourMySQLserverversionfortherightsyntaxtousenear
'SELECT(title,body)FROMblog_tableWHEREcat='OR1=1'atline1
該錯誤信息詳細地爲我們展示了完整的SQL語句,在此種情況下,SQL數據庫所期待的好象是一個整數,而非字符串,所以可以注入字符串OR1=1--,把單引號去掉就應該能成功注入了。對於大多數SQL數據庫,***者可以在一行中放入多個SQL語句,只要各個語句的語法沒有錯誤就行。在下面的代碼中,我們展示瞭如何將username設爲'OR1=1並把password設爲x來返回最後的用戶ID:
Stringquery="SELECTidFROMuser_tableWHERE"+
"username='"+username+"'AND"+
"password=PASSWORD('"+password+"')";
當然,***者可以注入其它的查詢,例如,把username設爲:
'OR1=1;DROPTABLEuser_table;--
而這個查詢將變成:
SELECTidFROMuser_tableWHEREusername=''OR1=1;DROPTABLEuser_table;--'ANDpassword=PASSWORD('x');
它相當於:
SELECTidFROMuser_tableWHEREusername=''OR1=1;DROPTABLEuser_table;
這個語句將執行句法上完全正確的SELECT語句,並利用SQLDROP命令清空user_table。
注射式***不必非要進行盲式***,因爲許多Web應用是利用開放源代碼工具開發的,爲了提高注射式***的成功率,我們可以下載免費的或者產品的試用版,然後在自己的系統上搭建測試系統。如果在測試系統上發現了錯誤,那麼很可能同樣的問題也會存在於所有使用該工具的Web應用身上。
五、小結
我們在本文中向讀者介紹了注射***的根本原因,即沒有對數據和命令進行嚴格區分。然後通過一些程序源碼對SQL的***進行了細緻的分析,使我們對SQL注射機理有了一個深入的認識。如果您是一名web應用開發人員,那麼您就當心了,一定不要盲目相信用戶端的輸入,而要對用戶輸入的數據進行嚴格的“消毒”處理,否則的話,SQL注射將會不期而至。
作者: 康凱 出處:51CTO.com
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章