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