高級Sql注入]
一個應用程序通常過濾單引號,另一方面限制用戶的輸入,比如限制長度。
在這裡,我們將討論一些繞過一些明顯的SQL注入防范的和長度限制的技巧。
[沒有符號的字符串]
有時候,開發人員可能已經通過過濾單引號來保護應用程序,比如用VBScript的'replace'函數:
function escape( input )
input = replace(input, "'", "''")
escape = input
end function
不可否認,這會阻止所有的對我們上面給出的對示例站點的攻擊,刪除';'字符也會起作用。但是,在一個大的程序裡一些用戶輸入可能被假定為數值型。這些值沒有限制,提供了很多可以注入的地方。
如果攻擊者希望創建一個字符串值而不使用引號,他們可以用'char'函數。例如:
insert into users values( 666,
char(0x63)+char(0x68)+char(0x72)+char(0x69)+char(0x73),
char(0x63)+char(0x68)+char(0x72)+char(0x69)+char(0x73),
0xffff)
它是一個往表裡插入字符的不帶引號的查詢語句。
當然,如果攻擊者使用一個數值型的用戶名和密碼的話,下面的語句也同樣可以很好的執行:
insert into users values( 667,
123,
123,
0xffff)
因為SQL-Server自動將數值型的轉換成'varchar'類型,類型轉換是默認的。
[SQL二次注入]
即使一個程序總是過濾單引號,攻擊者仍然可以先注入SQL作為數據存放在數據庫裡然後被程序再次使用。
比如,一個攻擊者可能通過注冊,創建一個用戶名
Username: admin'--
Password: passWord
程序正確的過濾了單引號,'insert'語句如下:
insert into users values ( 123, 'admin''--', 'passWord', 0xffff)
我們假設程序允許用戶更改密碼,ASP腳本在設置新的密碼前先確認用戶舊密碼正確。代碼可能這樣寫:
username = escape( Request.form("username") );
oldpassword = escape( Request.form("oldpassWord") );
newpassword = escape( Request.form("newpassWord") );
var rso = Server.CreateObject("ADODB.Recordset");
var sql = "select * from users where username = '" + username + "' and password = '" + oldpassWord + "'";
rso.open( sql, cn );
if (rso.EOF)
{
…
設置新密碼的查詢語句可能這樣寫的:
sql = "update users set password = '" + newpassWord + "' where username = '" + rso("username") + "'"
rso("username")是登陸的查詢返回的的用戶名。
用戶名為admin'--,上面的查詢就變成了這樣:
update users set password = 'passWord' where username = 'admin'--'
因此攻擊者可以通過注冊了一個名叫admin'--的用戶來把admin的密碼改成他們自己的。
這是個危險的問題,目前大部分的大型程序都試圖過濾數據。最好的解決方法是拒絕非法輸入,而不是簡單的改變它。這有時候會導致一些問題,非法字符在某些地方是必要的,比如在名字帶符號的情況:
O'BrIEn
從安全的角度,最好的解決辦法是不允許出現單引號。如果這樣不行,必須避免它們出現,這種情況下,最好保證所有要進入SQL語句的字符(包括從數據庫裡取出的字符)都被正確的處理過。