PHP+MYSQL網站SQL Injection攻防

程序員們寫代碼的時候講究TDD(測試驅動開發):在實現一個功能前,會先寫一個測試用例,而後再編寫代碼使之運行經過。其實當黑客SQL Injection時,一樣是一個TDD的過程:他們會先嚐試着讓程序報錯,而後一點一點的修正參數內容,當程序再次運行成功之時,注入也就隨之成功了。

進攻:

假設你的程序裏有相似下面內容的腳本:

$sql = "SELECT id, title, content FROM articles WHERE id = {$_GET[''id'']}";

正常訪問時其URL以下:

/articles.php?id=123

當黑客想判斷是否存在SQL Injection漏洞時,最經常使用的方式就是在整形ID後面加個單引號:

/articles.php?id=123''

因爲咱們沒有過濾$_GET[''id'']參數,因此必然會報錯,可能會是相似下面的信息:

supplied argument is not a valid MySQL result resource in ...

這些信息就足以說明腳本存在漏洞了,咱們能夠再耍點手段:

/articles.php?id=0 union select 1,2,3

之因此select 1,2,3是由於union要求兩邊的字段數一致,前面是id,title,content三個字段,後面1,2,3也是三個,因此不會報語法錯誤,還有設置id=0是一條不存在的記錄,那麼查詢的結果就是1,2,3,反映到網頁上,本來顯示id的地方會顯示1,顯示title的地方會顯示2,顯示content的地方會顯示3。

至於如何繼續利用,還要看magic_quotes_gpc的設置:

當magic_quotes_gpc爲off時

/articles.php?id=0 union select 1,2,load_file(''/etc/passwd'')

如此一來,/etc/passwd文件的內容就會顯示在本來顯示content的地方。

當magic_quotes_gpc爲on時

此時若是直接使用load_file(''/etc/passwd'')就無效了,由於單引號被轉義了,可是還有辦法:

/articles.php?id=0 union select 1,2,load_file(char(47,101,116,99,47,112,97,115,115,119,100))

其中的數字就是/etc/passwd字符串的ASCII:字符串每一個字符循環輸出ord(...)

除此覺得,還可使用字符串的十六進制:字符串每一個字符循環輸出dechex(ord(...))

/articles.php?id=0 union select 1,2,load_file(0x2f6574632f706173737764)

這裏僅僅說了數字型參數的幾種攻擊手段,屬於冰山一角,字符串型參數等攻擊手段看後面的文檔連接。

防守:

網絡上有一些相似SQL Injection Firewall的軟件可供使用,好比說GreenSQL,若是網站已經開始遭受SQL Injection攻擊,那麼使用這樣的快捷工具每每會救你一命,不過這樣的軟件在架構上屬於一個Proxy的角色,多半會影響網站併發性能,因此在選擇與否這個問題上最好視客觀條件來慎重決定。不少時候專業的軟件並非必須的,還有不少輕量級解決方案,下面演示一下如何使用awk來檢測可能的漏洞。

建立detect_sql_injection.awk腳本,內容以下(若是要拷貝一下內容的話記得不要包括行號):

01 #!/bin/gawk -f
02
03 /\$_(GET|POST|COOKIE|REQUEST)\s*\[/ {
04     IGNORECASE = 1
05     if (match($0, /\$.*(sql|query)/)) {
06         IGNORECASE = 0
07         output()
08         next
09     }
10 }
11
12 function output()
13 {
14     $1 = $1
15     print "CRUD: " $0 "\nFILE: " FILENAME "\nLINE: " FNR "\n"
16 }

此腳本可匹配出相似以下的問題代碼,想要擴展匹配模式也容易,只要照貓畫虎寫if match語句便可。

1:$sql = "SELECT * FROM users WHERE username = ''{$_POST[''username'']}''";
2:$res = mysql_query("SELECT * FROM users WHERE username = ''{$_POST[''username'']}''");

使用前別忘了先chmod +x detect_sql_injection.awk,有兩種調用方法:

1:./detect_sql_injection.awk /path/to/php/script/file
2:find /path/to/php/script/directory -name "*.php" | xargs ./detect_sql_injection.awk

會把有問題的代碼信息顯示出來,樣子以下:

CRUD: $sql = "SELECT * FROM users WHERE username = ''{$_POST[''username'']}''";
FILE: /path/to/file.php
LINE: 123

現實環境中有不少應用這個腳本的方法,好比說經過CRON按期掃描程序源文件,或者在SVN提交時經過鉤子方法自動匹配。

使用專業工具也好,檢測腳本亦罷,都是被動的防守,問題的根本始終取決於在程序員頭腦裏是否有必要的安全意識,下面是一些必需要牢記的準則:

1:數字型參數使用相似intval,floatval這樣的方法強制過濾。
2:字符串型參數使用相似mysql_real_escape_string這樣的方法強制過濾,而不是簡單的addslashes。
3:最好拋棄mysql_query這樣的拼接SQL查詢方式,儘量使用PDO的prepare綁定方式。
4:使用rewrite技術隱藏真實腳本及參數的信息,經過rewrite正則也能過濾可疑的參數。
5:關閉錯誤提示,不給攻擊者提供敏感信息:display_errors=off。
6:以日誌的方式記錄錯誤信息:log_errors=on和error_log=filename,按期排查,Web日誌最好也查。
7:不要用具備FILE權限的帳號(好比root)鏈接MySQL,這樣就屏蔽了load_file等危險函數。
8:......

網站安全其實並不複雜,總結出來就是一句話:過濾輸入,轉義輸出。其中,咱們上面一直討論的SQL Injection問題就屬於過濾輸入問題,至於轉義輸出問題,其表明是Cross-site scripting,但它不屬於本文的範疇,就很少說了。

文檔:

addslashes() Versus mysql_real_escape_string()
SQL Injection with MySQL
Advanced SQL Injection with MySQL
MySQL注入中導出字段內容的研究——經過注入導出WebShell
php

轉載自:http://www.aspnetjia.commysql

相關文章
相關標籤/搜索