這篇文章討論常用的sql注入技術的細節(jié),應用于流行的Ms IIS/ASP/SQL-Server平臺。這里探討有關這種攻擊各種可以注入程序訪問數(shù)據(jù)和數(shù)據(jù)庫防范的方法。
[概 要]
這篇文章討論常用的”sql注入”技術的細節(jié),應用于流行的Ms IIS/ASP/SQL-Server平臺。這里探討有關這種攻擊各種可以注入程序訪問數(shù)據(jù)和數(shù)據(jù)庫防范的方法。這篇文章面向兩種讀者:一是基于數(shù)據(jù)庫web程序開發(fā)人員和審核各種web程序的安全專家。
[介 紹]
結構化查詢語言(SQL)是一種用來和數(shù)據(jù)庫交互的文本語言SQL語言多種多樣,大多的方言版本都共同寬松地遵循SQL-92標準(最新的ANSI標準[譯者注:目前最新的是SQL-99])。SQL運行的典型的操作是“查詢”,它是可以讓數(shù)據(jù)庫返回“查詢結果記錄集”的語句集合。SQL語句可以修改數(shù)據(jù)庫的結構(用數(shù)據(jù)定義語言”DDL”)和操作數(shù)據(jù)庫里的數(shù)據(jù)(用數(shù)據(jù)操作語言”DML”)。我們在這里著重討論Transact-SQL(交互式SQL),應用于SQL-Server的SQL一種方言(非標準SQL)。如果攻擊者可以插一系列的SQL語句進入應用程序的數(shù)據(jù)查詢時,Sql注入攻擊就可能發(fā)生。
一個典型的SQL語句是這樣的:
select id, forename, surname from authors
這個查詢語句將會從’authors’表中返回’id’,’forename’和’surname’列的所有行。返回的結果集也可以加以特定條件’author’限制:
select id, forename, surname from authors where forename = ‘john’ and surname = ‘smith’
注意這里很重要的一點是’john’和’smith’是被單引號引住的,假設’forename’和’surname’字段是來自于用戶的輸入,攻擊者就可能通過輸入非法字符串來對這個查詢進行SQL注入:
Forename:jo’hn
Surname: smith
查詢語句就會變成:
select id, forename, surname from authors where forename = ‘jo’hn’ and surname = ‘smith’
當數(shù)據(jù)庫試圖執(zhí)行這個查詢,它會返回這樣的錯誤:
Server:Msg 170, Level 15, State 1, Line 1
Line 1:Incorrect syntax near ‘hn’
這是因為插入的單引號破壞了原來單引號引住的數(shù)據(jù),數(shù)據(jù)庫執(zhí)行到’hn’時失敗。如果攻擊者這樣輸入:
Forename: jo’; drop table authors–
Surname:
…authors表就會被刪掉,原因過一會再解釋。
似乎通過刪除用戶輸入的字符串中的單引號或者通過一些方法避免它們出現(xiàn)可以解決這個問題。誠然如此,但是要實施這個解決方法還有很多的困難。因為首先:不是所有的用戶提交的數(shù)據(jù)都是字符串形式,比如我們的用戶輸入通過’id'(看上去是個數(shù)字)來選擇一個用戶,我們的查詢可能會這樣:
select id,forename,surname from authors where id=1234
在這種情況下攻擊者可以輕易的在數(shù)值輸入后面添加SQL語句。在其他SQL方言中,使用著各種分隔符,比如MS Jet DBMS引擎,日期可以用’#’符號來分隔。
其次,避免單引號并不像開始我們想象的那樣是必要的解決辦法,原因下面討論。
我們將以Active Server Pages(ASP)登陸頁面為例子來詳細說明,它訪問一個Sql-Server數(shù)據(jù)庫并且驗證一個到我們假想的程序的訪問。
這是用戶填寫用戶名和密碼的表單頁面:
代碼如下:
Login
這是’process_login.asp’的代碼, 它處理用戶登陸:
代碼如下:
function trace( str )
{
if( Request.form(“debug”) == “true” )
Response.write( str );
}
function Login( cn )
{
var username;
var password;
username = Request.form(“username”);
password = Request.form(“password”);
var rso = Server.CreateObject(“ADODB.Recordset”);
var sql = “select * from users where username = ‘” + username + “‘
and password = ‘” + password + “‘”;
trace( “query: ” + sql );
rso.open( sql, cn );
if (rso.EOF)
{
rso.close();
%>
Response.end
return;
}
else
{
Session(“username”) = “” + rso(“username”);
%>
ACCESS GRANTED
Welcome,
Response.write( “
Welcome,
Response.write( “