不管用什麼語言編寫的Web應用,它們都用一個共同點,具有交互性並且多數是數據庫驅動。在網絡中,數據庫驅動的Web應用隨處可見,由此而存在的SQL注入是影響企業運營且最具破壞性的漏洞之一,這裏我想問,我們真的瞭解SQL注入嗎?看完本篇文章希望能讓你更加深刻的認識SQL注入。
注入***原理及自己編寫注入點
1.1、什麼是SQL?
SQL 是一門 ANSI 的標準計算機語言,用來訪問和操作數據庫系統。SQL 語句用於取回和更新數據庫中的數據。SQL 可與數據庫程序協同工作,比如 MS Access、DB2、Informix、MS SQL Server、Oracle、Sybase 以及其他數據庫系統。
1.2、什麼是SQL注入?
看起來很複雜,其實很簡單就能解釋,SQL注入就是一種通過操作輸入來修改後臺SQL語句達到代碼執行進行***目的的技術。
1.3、SQL注入是怎麼樣產生的?
構造動態字符串是一種編程技術,它允許開發人員在運行過程中動態構造SQL語句。開發人員可以使用動態SQL來創建通用、靈活的應用。動態SQL語句是在執行過程中構造的,它根據不同的條件產生不同的SQL語句。當開發人員在運行過程中需要根據不同的查詢標準來決定提取什麼字段(如SELECT語句),或者根據不同的條件來選擇不同的查詢表時,動態構造SQL語句會非常有用。
在PHP中動態構造SQL語句字符串:
1 | $query = "SELECT * FROM users WHERE username = " . $_GET [ "klmyoil" ]; |
看上面代碼我們可以控制輸入參數klmyoil,修改所要執行SQL語句,達到***的目的。
1.4、編寫注入點
爲了照顧一下新人,這裏先介紹一下涉及到的基礎知識:
1 2 3 | SQL SELECT 語法 SELECT 列名稱 FROM 表名稱 符號 * 取代列的名稱是選取所有列 |
1 2 3 4 | WHERE 子句 如需有條件地從表中選取數據,可將 WHERE 子句添加到 SELECT 語句。 語法 SELECT 列名稱 FROM 表名稱 WHERE 列 運算符 值 |
下面的運算符可在 WHERE 子句中使用:
操作符 | 描述 |
= | 等於 |
<> | 不等於 |
> | 大於 |
< | 小於 |
>= | 大於等於 |
<= | 小於等於 |
BETWEEN | 在某個範圍內 |
LIKE | 搜索某種模式 |
瞭解了以上基礎知識就讓我們來自己編寫注入點把。
第一步:我們使用if語句來先判斷一下變量是否初始化
1 2 3 4 5 | <?php if (isset( $_GET [ "klmyoil" ])){ } ?> |
第二步:在if語句裏面,我們連接數據庫。在PHP中,這個任務通過 mysql_connect() 函數完成。
1 2 3 4 | mysql_connect(servername,username,password); servername 可選。規定要連接的服務器。默認是 "localhost:3306" 。 username 可選。規定登錄所使用的用戶名。默認值是擁有服務器進程的用戶的名稱。 password 可選。規定登錄所用的密碼。默認是 "" 。 |
第三步:連接成功後,我們需要選擇一個數據庫。
1 2 3 | mysql_select_db(database,connection) database 必需。規定要選擇的數據庫。 connection 可選。規定 MySQL 連接。如果未指定,則使用上一個連接。 |
第四步:選擇完數據庫,我們需要執行一條 MySQL 查詢。
1 2 3 | mysql_query(query,connection) query 必需。規定要發送的 SQL 查詢。註釋:查詢字符串不應以分號結束。 connection 可選。規定 SQL 連接標識符。如果未規定,則使用上一個打開的連接。 |
第五步:執行完查詢,我們再對結果進行處理
1 2 3 4 5 6 7 | mysql_fetch_array ( data , array_type ) data 可選。規定要使用的數據指針。該數據指針是 mysql_query ( ) 函數產生的結果。 array_type 可選。規定返回哪種結果。可能的值:MYSQL_ASSOC - 關聯數組 MYSQL_NUM - 數字數組 MYSQL_BOTH - 默認。同時產生關聯和數字數組 |
題外話:我們使用echo將執行的SQL語句輸出,方便我們查看後臺執行了什麼語句。
1 | echo $querry |
最終代碼如下:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | if (isset( $_GET [ "id" ])){ $con = mysql_connect( "127.0.0.1:3306" , "root" , "root" ); if (! $con ) { die ( 'Could not connect: ' . mysql_error()); } mysql_select_db( "klmyoil" , $con ); $querry = "select * from users where id = " . $_GET [ 'id' ]; $sql = mysql_query( $querry , $con ); $result = mysql_fetch_array( $sql ); echo "<table class='itable' border='1' cellspacing='0' width='300px' height='150'>" ; echo "<tr>" ; echo "<td>id</td>" ; echo "<td>username</td>" ; echo "</tr>" ; echo "<tr>" ; echo "<td>" . $result [ 'id' ]. "</td>" ; echo "<td>" . $result [ 'username' ]. "</td>" ; echo "</tr>" ; echo "</table>" ; mysql_close( $con ); echo $querry ; } ?> |
MySQL數據庫實驗環境配置:
代碼層工作已經做好,但是在數據庫裏面,我們還沒有klmyoil這個數據庫啊,接下來我就帶大家一步步創建數據庫,創建表,創建列,插入數據。
第一步:創建數據庫
第二步:創建表users和列id,username,password
第三步:我們插入幾條數據
同樣的道理,大家多插幾條數據。到此我們整個任務就完成了。
最終成果如下:
尋找及確認SQL注入
推理測試法
尋找SQL注入漏洞有一種很簡單的方法,就是通過發送特殊的數據來觸發異常。
首先我們需要了解數據是通過什麼方式進行輸入,這裏我總結了三個:
GET請求:該請求在URL中發送參數。
POST請求:數據被包含在請求體中。
其他注入型數據:HTTP請求的其他內容也可能會觸發SQL注入漏洞。
瞭解完數據的輸入方式,我們接下來再學習數據庫錯誤。這裏我們以MySQL爲例,其它的請大家自行學習咯。
我們現在參數後面加個單引號,如下圖:
sql語句最終變爲
1 | select * from users where id = 1' |
執行失敗,所以mysql_query()函數會返回一個布爾值,在下行代碼中mysql_fetch_array($sql)將執行失敗,並且PHP會顯示一條警告信息,告訴我們mysql_fetch_array()的第一個參數必須是個資源,而代碼在實際運行中,給出的參數值卻是一個布爾值。
我們修改代碼在
1 2 | $sql = mysql_query( $querry , $con );下一行加上 var_dump( $sql ); |
可以發現:
爲了更好的瞭解MySQL錯誤,我們在
?
1 | $sql = mysql_query( $querry , $con ); |
加上
[PHP] 純文本查看 複製代碼
?
1 2 3 4 | if (! $sql ) { die ( '<p>error:' .mysql_error(). '</p>' ); } |
這樣當應用捕獲到數據庫錯誤且SQL查詢失敗時,就會返回錯誤信息:(我們在參數中添加單引號返回的錯誤信息)
1 | error:You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' ' at line 1 |
然後藉助這些錯誤,我們這可以推斷應該存在SQL注入。還有其他數據庫錯誤信息,以及MySQL其他錯誤信息,由於篇幅問題就不一一講解了。
2.2、and大法和or大法
頁面不返回任何錯誤信息,我們就可以藉助本方法來推斷了,首先我們在參數後面加上 and 1=1和and 1=2看看有什麼不同
可以發現and 1=1 返回了數據,而and 1=2沒有,這是由於1=1是一個爲真的條件,前面的結果是true,true and true 所以沒有任何問題,第二個 1=2 是個假條件, true and false還是false,所以並沒有數據返回。
好,講完and,我們自來看看 or ,or就是或者,兩個都爲假,纔會爲假。我們先把id改爲5,可以發現id=5是沒有數據的。
可以發現我們加上or 1=1就成功返回了數據,這是因爲1=1爲真,不管前面是不是假,數據都會返回,這樣就把表裏面數據全部返回,我們沒看見,是因爲代碼中並沒有迭代輸出。這樣,我們來修改一下代碼。
01 02 03 04 05 06 07 08 09 10 11 12 | echo "<table class='itable' border='1' cellspacing='0' width='300px' height='150'>" ; echo "<tr>" ; echo "<td>id</td>" ; echo "<td>username</td>" ; echo "</tr>" ; //遍歷查詢結果 while ( $result = mysql_fetch_array( $sql )) { echo "<tr>" ; echo "<td>" . $result [0] . "</td>" ; echo "<td>" . $result [1] . "</td>" ; echo "</tr>" ; } |
然後你就可以發現:
2.3、加法和減法
這裏我們需要區分一下數字型和字符串型:
數字型:不需要使用單引號來表示
其他類型:使用單引號來表示
綜合上述,我們可以發現我們的例子是數字型的,這樣我們就可以使用加法和減法來判斷了。
加法,我們在參數輸入1+1,看看返回的數據是不是id等於2的結果,這裏注意一下+號在SQL語句是有特效含義的,所以我們要對其進行url編碼,最後也就是%2b。
減法是同樣的道理,不過我們不需要對-號進行url編碼了。