轉(zhuǎn) Web應(yīng)用的輸入攻擊及其防范
來自:http://www.vbyte.com/blog/detail.asp?blog_id=1&content_id=3&cat_id=7
一、攻擊者繞過身份驗(yàn)證的方法?
設(shè)想有這樣一個(gè)簡單的用戶登錄程序:它通過表單獲取用戶名字和密碼,然而把這些數(shù)據(jù)發(fā)送到服務(wù)器端,服務(wù)器端程序查找數(shù)據(jù)庫并驗(yàn)
證用戶身份。這種用戶身份驗(yàn)證方法非常常見。檢查用戶名字和密碼時(shí),許多開發(fā)者使用的是類如下面的代碼:?
<HTML>
<%@language=javascript....%>
<%
if?(isPasswordOK(Request.form("name"),Request.form("pwd")))?{
Response.write("身份驗(yàn)證通過!");
//?其他操作
}?else?{
Response.write("拒絕訪問!");
}
function?isPasswordOK(strName,?strPwd)?{
var?fAllowLogon?=?false;
try?{
var?oConn?=?new?ActiveXObject("ADODB.Connection");
var?strConnection="Provider=Microsoft.Jet.OLEDB.4.0;Data
Source=c:\\auth\\auth.mdb;"
oConn.Open(strConnection);
var?strSQL?=?"SELECT?count(*)?FROM?client?WHERE?"?+
"(name='"?+?strName?+?"')?"?+?
"?and?"?+
"(pwd='"?+?strPwd?+?"')";
var?oRS?=?new?ActiveXObject("ADODB.RecordSet");
oRS.Open(strSQL,oConn);
fAllowLogon?=?(oRS(0).Value?>?0)???true?:?false;
oRS.Close();
delete?oRS;
oConn.Close();
delete?oConn;
}?catch(e)?{
fAllowLogon?=?false;
}
return?fAllowLogon;
}
%>
</HTML>
注意,在上面的代碼中,程序從表單數(shù)據(jù)中提取出用戶名字和密碼,然后直接傳遞給了進(jìn)行驗(yàn)證的函數(shù)isPasswordOK。查詢數(shù)據(jù)庫的SQL命
令直接利用表單輸入數(shù)據(jù)構(gòu)造,這是一個(gè)不安全的過程。假設(shè)用戶名字是“mikey”,密碼是“&y-)4Hi=Qw8”,則實(shí)際查詢數(shù)據(jù)庫的SQL命令是
:?
SELECT?count(*)?FROM?client?WHERE?(name='mikey')?and
(pwd='&y-)4Hi=Qw8')
當(dāng)查詢結(jié)果中count(*)返回合適的數(shù)值時(shí),用戶“mikey”的身份驗(yàn)證通過。如果count(*)返回的結(jié)果是0,則查詢未能找到匹配的記錄,
用戶被拒絕訪問。?
那么,攻擊者如何繞過這個(gè)驗(yàn)證過程呢?首先,攻擊者會(huì)假定驗(yàn)證過程使用上面這種SQL命令進(jìn)行驗(yàn)證,然后他們就發(fā)送偽造的用戶名字和
密碼,改變SQL命令的判斷邏輯。例如,如果攻擊者輸入的用戶名字是“x'?or?1)?or?('1”,密碼是“anyoldpassword”,則實(shí)際查詢數(shù)據(jù)庫
的SQL命令將變成:?
SELECT?count(*)?FROM?client?WHERE?(name='x'?or?1)?or?('1')
and?(pwd='anyoldpassword')
可以看出,這個(gè)查詢選出的行數(shù)量總是大于或等于1,即count(*)的返回結(jié)果總是1或者更大。由此,雖然攻擊者并不知道合法的用戶名字
和密碼,他總是能夠通過身份驗(yàn)證!?
注意下面5點(diǎn)有助于防止攻擊者繞過Web應(yīng)用的身份驗(yàn)證過程。?
二、防止錯(cuò)誤信息泄密?
不要向?yàn)g覽器返回帶有出錯(cuò)SQL命令的錯(cuò)誤信息。出現(xiàn)腳本錯(cuò)誤時(shí),默認(rèn)情況下IIS會(huì)返回一個(gè)500-100錯(cuò)誤信息。事實(shí)上,返回不帶調(diào)試信
息的錯(cuò)誤信息更有利于安全。例如,當(dāng)我們?cè)诘卿洷韱屋斎雮卧斓挠脩裘趾兔艽a時(shí),服務(wù)器返回的錯(cuò)誤信息可能如下:?
Error?Type:
Microsoft?JET?Database?Engine?(0x80040E14)
Syntax?error?(missing?operator)?in?query?expression?
'(name='x'?or?1)?or?('1')?and?(pwd=''p')'.
/login.asp,?line?24
可以看到,錯(cuò)誤信息中包含了部分SQL命令,它會(huì)幫助攻擊者改正原先的錯(cuò)誤,為攻擊者快速構(gòu)造出雖然偽造、但在SQL命令中合法的用戶
名字和密碼帶來了方便。?
要減少服務(wù)器返回的錯(cuò)誤信息量,最簡單的方法是修改%winnt%\help\iisHelp\500-100.asp;或者創(chuàng)建一個(gè)新的文件,同時(shí)設(shè)置IIS,使得
500.100錯(cuò)誤出現(xiàn)時(shí)服務(wù)器返回這個(gè)新的文件。設(shè)置IIS服務(wù)器的步驟如下:?
1.打開IIS管理工具?
2.右擊要設(shè)置的Web服務(wù)器?
3.選擇“屬性”?
4.選擇定制錯(cuò)誤的選項(xiàng)卡?
5.輸入定制的500.100錯(cuò)誤頁面文件的名字?
對(duì)于安全來說,錯(cuò)誤信息中最好永遠(yuǎn)不要出現(xiàn)服務(wù)器上的物理路徑。例如,返回“不能在c:\wwwroot\secretlocation目錄下找到foo.doc
文件”這種錯(cuò)誤信息并不有利于安全,一個(gè)簡單的404提示已經(jīng)足夠了。?
三、確定合法性規(guī)則?
利用VBscript....、Jscript....以及Perl語言的正則表達(dá)式,我們可以為用戶輸入數(shù)據(jù)定義合法性規(guī)則。不要去分析哪些輸入數(shù)據(jù)非法,因?yàn)楣?br />
者會(huì)找出繞過非法數(shù)據(jù)檢查規(guī)則的辦法。例如,假設(shè)為了防止用戶向網(wǎng)站發(fā)送HTML數(shù)據(jù),我們要替換輸入數(shù)據(jù)中的“<”和“>”符號(hào):?
strInput?=?strInput.replace(/[<>]/,"");?
上面這個(gè)語句把“<”和“>”符號(hào)替換成空字符串。那么,是否這樣一來攻擊者所發(fā)送的HTML就不能再發(fā)送到服務(wù)器上了呢?答案是否定
的。攻擊者只需把“<”和“>”替換為相應(yīng)的HTML實(shí)體符號(hào),上述分析用戶輸入的代碼就不能再找出HTML。由此我們認(rèn)識(shí)到,正確的方法應(yīng)該
是先確定什么是合法的,然后驗(yàn)證用戶輸入的合法性,拒絕所有不符合合法性規(guī)則的輸入:?
if?(strName.search(/[^A-Za-z?0-9]/)?!=?-1)?return?false;?
這行代碼搜索strName,如果strName包含除了大寫字母、小寫字母、數(shù)字和空白字符之外的(這就是^的含義)字符,則輸入數(shù)據(jù)被拒絕。?
進(jìn)行輸入檢驗(yàn)時(shí)還要注意偽造的文件名字。攻擊者可能嘗試把數(shù)據(jù)發(fā)送到某些敏感的位置,或者可能發(fā)出請(qǐng)求試圖得到源文件,等等。下
面這個(gè)正則表達(dá)式對(duì)文件名字作嚴(yán)格的限制。合法文件名字的規(guī)則描述如下:?
●一個(gè)或者多個(gè)0-9a-zA-Z或_,再加上?
●一個(gè)或者多個(gè)0-9a-zA-Z、-、\、/和_,再加上?
●一個(gè)句點(diǎn),再加上?
●’’、’txt’、’jpg’、’jpeg’、’gif’、’htm’、’html’、’png’、’bmp’或’zip’?
所有不符合上述規(guī)則的文件名字都非法。這個(gè)文件名字規(guī)則非常嚴(yán)格,但確實(shí)有效。你可以看到,文件名字不能以斜杠開頭,因?yàn)樾备苁?br />
磁盤的根目錄。除了攻擊者之外,另外還有誰需要從磁盤的根目錄開始訪問呢?所有對(duì)文件的訪問都應(yīng)該相對(duì)于Web網(wǎng)站的根進(jìn)行:?
var?strInput?=?Request.form("filename");?
var?re?=?/^[\w]{1,}[\w\-\/\\]{1,}\.(txt|jpg|jpeg|gif|htm|html|png|bmp|zip)?
{0,1}$/i;?
var?fIsFilenameValid?=?(re.test(strInput))???true?:?false;?
四、正確處理引號(hào)?
引號(hào)有時(shí)候很難處理,因?yàn)樗鼈儠?huì)干擾SQL命令。如本文開頭的例子,攻擊者利用引號(hào)改變SQL命令的邏輯,使得不具備合法用戶名字和密
碼的人也能夠登錄系統(tǒng)。防止利用引號(hào)攻擊的另外一種方法是事先轉(zhuǎn)義引號(hào)字符。下面這個(gè)正則表達(dá)式將把所有單引號(hào)和雙引號(hào)分別替換為兩
個(gè)單引號(hào)和兩個(gè)雙引號(hào)。替換得到的SQL命令完全合乎SQL語法,而且它使得許多攻擊更難進(jìn)行。?
strPwd?=?strPwd.replace(/([\’\"])/g,"$1$1");?
這個(gè)語句可以替換前面加上的正則表達(dá)式。但也可以兩者一起使用,加強(qiáng)防衛(wèi)力量!?
五、檢查SQL查詢返回的數(shù)據(jù)?
一種完全防止這類攻擊的方法是停止使用只能表示“贊成、反對(duì)”的count(*),改為檢查用戶名字、密碼是否和SQL命令返回的用戶名字、
密碼匹配。具體代碼如下所示:?
var?strSQL?=?"SELECT?name,?pwd?FROM?client?WHERE?"?+?
"(name=’"?+?strName?+?"’)?"?+?
"?and?"?+?
"(pwd=’"?+?strPwd?+?"’)";?
var?oRS?=?new?ActiveXObject("ADODB.RecordSet");?
oRS.Open(strSQL,oConn);?
fAllowLogon?=?(oRS(0).Value?==?strName?&&?oRS(1).Value?==?strPwd)?
??true?:?false;?
如果SQL查詢沒有返回?cái)?shù)據(jù),程序?qū)⒂|發(fā)一個(gè)異常,隨后這個(gè)異常就被catch()捕獲。?
六、禁用父路徑?
確保文件名字中沒有出現(xiàn)“..”。按照如下步驟禁用父路徑:?
右擊Web網(wǎng)站的根,從菜單選擇“屬性”。?
●選擇“主目錄”選項(xiàng)卡。?
●點(diǎn)擊“配置”。?
●點(diǎn)擊“應(yīng)用程序選項(xiàng)”。?
●取消“啟用父路徑”。?
如果要從命令行禁用父路徑,請(qǐng)執(zhí)行如下命令:?
cscript....?adsutil.vbs?set?w3svc/1/root/AspEnableParentPaths?false?
要真正做到對(duì)輸入攻擊的全面防范,你必須有一切輸入數(shù)據(jù)都可能有危險(xiǎn)的心理準(zhǔn)備。檢查合法的輸入,而不是檢查不合法的輸入,因?yàn)?br />
攻擊者很快就可以找出突破不合法規(guī)則的辦法。同時(shí),學(xué)習(xí)并正確地運(yùn)用正則表達(dá)式。記住了這幾點(diǎn),你就能夠大大減少Web應(yīng)用被侵入的機(jī)會(huì)。?
設(shè)想有這樣一個(gè)簡單的用戶登錄程序:它通過表單獲取用戶名字和密碼,然而把這些數(shù)據(jù)發(fā)送到服務(wù)器端,服務(wù)器端程序查找數(shù)據(jù)庫并驗(yàn)
證用戶身份。這種用戶身份驗(yàn)證方法非常常見。檢查用戶名字和密碼時(shí),許多開發(fā)者使用的是類如下面的代碼:?
<HTML>
<%@language=javascript....%>
<%
if?(isPasswordOK(Request.form("name"),Request.form("pwd")))?{
Response.write("身份驗(yàn)證通過!");
//?其他操作
}?else?{
Response.write("拒絕訪問!");
}
function?isPasswordOK(strName,?strPwd)?{
var?fAllowLogon?=?false;
try?{
var?oConn?=?new?ActiveXObject("ADODB.Connection");
var?strConnection="Provider=Microsoft.Jet.OLEDB.4.0;Data
Source=c:\\auth\\auth.mdb;"
oConn.Open(strConnection);
var?strSQL?=?"SELECT?count(*)?FROM?client?WHERE?"?+
"(name='"?+?strName?+?"')?"?+?
"?and?"?+
"(pwd='"?+?strPwd?+?"')";
var?oRS?=?new?ActiveXObject("ADODB.RecordSet");
oRS.Open(strSQL,oConn);
fAllowLogon?=?(oRS(0).Value?>?0)???true?:?false;
oRS.Close();
delete?oRS;
oConn.Close();
delete?oConn;
}?catch(e)?{
fAllowLogon?=?false;
}
return?fAllowLogon;
}
%>
</HTML>
注意,在上面的代碼中,程序從表單數(shù)據(jù)中提取出用戶名字和密碼,然后直接傳遞給了進(jìn)行驗(yàn)證的函數(shù)isPasswordOK。查詢數(shù)據(jù)庫的SQL命
令直接利用表單輸入數(shù)據(jù)構(gòu)造,這是一個(gè)不安全的過程。假設(shè)用戶名字是“mikey”,密碼是“&y-)4Hi=Qw8”,則實(shí)際查詢數(shù)據(jù)庫的SQL命令是
:?
SELECT?count(*)?FROM?client?WHERE?(name='mikey')?and
(pwd='&y-)4Hi=Qw8')
當(dāng)查詢結(jié)果中count(*)返回合適的數(shù)值時(shí),用戶“mikey”的身份驗(yàn)證通過。如果count(*)返回的結(jié)果是0,則查詢未能找到匹配的記錄,
用戶被拒絕訪問。?
那么,攻擊者如何繞過這個(gè)驗(yàn)證過程呢?首先,攻擊者會(huì)假定驗(yàn)證過程使用上面這種SQL命令進(jìn)行驗(yàn)證,然后他們就發(fā)送偽造的用戶名字和
密碼,改變SQL命令的判斷邏輯。例如,如果攻擊者輸入的用戶名字是“x'?or?1)?or?('1”,密碼是“anyoldpassword”,則實(shí)際查詢數(shù)據(jù)庫
的SQL命令將變成:?
SELECT?count(*)?FROM?client?WHERE?(name='x'?or?1)?or?('1')
and?(pwd='anyoldpassword')
可以看出,這個(gè)查詢選出的行數(shù)量總是大于或等于1,即count(*)的返回結(jié)果總是1或者更大。由此,雖然攻擊者并不知道合法的用戶名字
和密碼,他總是能夠通過身份驗(yàn)證!?
注意下面5點(diǎn)有助于防止攻擊者繞過Web應(yīng)用的身份驗(yàn)證過程。?
二、防止錯(cuò)誤信息泄密?
不要向?yàn)g覽器返回帶有出錯(cuò)SQL命令的錯(cuò)誤信息。出現(xiàn)腳本錯(cuò)誤時(shí),默認(rèn)情況下IIS會(huì)返回一個(gè)500-100錯(cuò)誤信息。事實(shí)上,返回不帶調(diào)試信
息的錯(cuò)誤信息更有利于安全。例如,當(dāng)我們?cè)诘卿洷韱屋斎雮卧斓挠脩裘趾兔艽a時(shí),服務(wù)器返回的錯(cuò)誤信息可能如下:?
Error?Type:
Microsoft?JET?Database?Engine?(0x80040E14)
Syntax?error?(missing?operator)?in?query?expression?
'(name='x'?or?1)?or?('1')?and?(pwd=''p')'.
/login.asp,?line?24
可以看到,錯(cuò)誤信息中包含了部分SQL命令,它會(huì)幫助攻擊者改正原先的錯(cuò)誤,為攻擊者快速構(gòu)造出雖然偽造、但在SQL命令中合法的用戶
名字和密碼帶來了方便。?
要減少服務(wù)器返回的錯(cuò)誤信息量,最簡單的方法是修改%winnt%\help\iisHelp\500-100.asp;或者創(chuàng)建一個(gè)新的文件,同時(shí)設(shè)置IIS,使得
500.100錯(cuò)誤出現(xiàn)時(shí)服務(wù)器返回這個(gè)新的文件。設(shè)置IIS服務(wù)器的步驟如下:?
1.打開IIS管理工具?
2.右擊要設(shè)置的Web服務(wù)器?
3.選擇“屬性”?
4.選擇定制錯(cuò)誤的選項(xiàng)卡?
5.輸入定制的500.100錯(cuò)誤頁面文件的名字?
對(duì)于安全來說,錯(cuò)誤信息中最好永遠(yuǎn)不要出現(xiàn)服務(wù)器上的物理路徑。例如,返回“不能在c:\wwwroot\secretlocation目錄下找到foo.doc
文件”這種錯(cuò)誤信息并不有利于安全,一個(gè)簡單的404提示已經(jīng)足夠了。?
三、確定合法性規(guī)則?
利用VBscript....、Jscript....以及Perl語言的正則表達(dá)式,我們可以為用戶輸入數(shù)據(jù)定義合法性規(guī)則。不要去分析哪些輸入數(shù)據(jù)非法,因?yàn)楣?br />
者會(huì)找出繞過非法數(shù)據(jù)檢查規(guī)則的辦法。例如,假設(shè)為了防止用戶向網(wǎng)站發(fā)送HTML數(shù)據(jù),我們要替換輸入數(shù)據(jù)中的“<”和“>”符號(hào):?
strInput?=?strInput.replace(/[<>]/,"");?
上面這個(gè)語句把“<”和“>”符號(hào)替換成空字符串。那么,是否這樣一來攻擊者所發(fā)送的HTML就不能再發(fā)送到服務(wù)器上了呢?答案是否定
的。攻擊者只需把“<”和“>”替換為相應(yīng)的HTML實(shí)體符號(hào),上述分析用戶輸入的代碼就不能再找出HTML。由此我們認(rèn)識(shí)到,正確的方法應(yīng)該
是先確定什么是合法的,然后驗(yàn)證用戶輸入的合法性,拒絕所有不符合合法性規(guī)則的輸入:?
if?(strName.search(/[^A-Za-z?0-9]/)?!=?-1)?return?false;?
這行代碼搜索strName,如果strName包含除了大寫字母、小寫字母、數(shù)字和空白字符之外的(這就是^的含義)字符,則輸入數(shù)據(jù)被拒絕。?
進(jìn)行輸入檢驗(yàn)時(shí)還要注意偽造的文件名字。攻擊者可能嘗試把數(shù)據(jù)發(fā)送到某些敏感的位置,或者可能發(fā)出請(qǐng)求試圖得到源文件,等等。下
面這個(gè)正則表達(dá)式對(duì)文件名字作嚴(yán)格的限制。合法文件名字的規(guī)則描述如下:?
●一個(gè)或者多個(gè)0-9a-zA-Z或_,再加上?
●一個(gè)或者多個(gè)0-9a-zA-Z、-、\、/和_,再加上?
●一個(gè)句點(diǎn),再加上?
●’’、’txt’、’jpg’、’jpeg’、’gif’、’htm’、’html’、’png’、’bmp’或’zip’?
所有不符合上述規(guī)則的文件名字都非法。這個(gè)文件名字規(guī)則非常嚴(yán)格,但確實(shí)有效。你可以看到,文件名字不能以斜杠開頭,因?yàn)樾备苁?br />
磁盤的根目錄。除了攻擊者之外,另外還有誰需要從磁盤的根目錄開始訪問呢?所有對(duì)文件的訪問都應(yīng)該相對(duì)于Web網(wǎng)站的根進(jìn)行:?
var?strInput?=?Request.form("filename");?
var?re?=?/^[\w]{1,}[\w\-\/\\]{1,}\.(txt|jpg|jpeg|gif|htm|html|png|bmp|zip)?
{0,1}$/i;?
var?fIsFilenameValid?=?(re.test(strInput))???true?:?false;?
四、正確處理引號(hào)?
引號(hào)有時(shí)候很難處理,因?yàn)樗鼈儠?huì)干擾SQL命令。如本文開頭的例子,攻擊者利用引號(hào)改變SQL命令的邏輯,使得不具備合法用戶名字和密
碼的人也能夠登錄系統(tǒng)。防止利用引號(hào)攻擊的另外一種方法是事先轉(zhuǎn)義引號(hào)字符。下面這個(gè)正則表達(dá)式將把所有單引號(hào)和雙引號(hào)分別替換為兩
個(gè)單引號(hào)和兩個(gè)雙引號(hào)。替換得到的SQL命令完全合乎SQL語法,而且它使得許多攻擊更難進(jìn)行。?
strPwd?=?strPwd.replace(/([\’\"])/g,"$1$1");?
這個(gè)語句可以替換前面加上的正則表達(dá)式。但也可以兩者一起使用,加強(qiáng)防衛(wèi)力量!?
五、檢查SQL查詢返回的數(shù)據(jù)?
一種完全防止這類攻擊的方法是停止使用只能表示“贊成、反對(duì)”的count(*),改為檢查用戶名字、密碼是否和SQL命令返回的用戶名字、
密碼匹配。具體代碼如下所示:?
var?strSQL?=?"SELECT?name,?pwd?FROM?client?WHERE?"?+?
"(name=’"?+?strName?+?"’)?"?+?
"?and?"?+?
"(pwd=’"?+?strPwd?+?"’)";?
var?oRS?=?new?ActiveXObject("ADODB.RecordSet");?
oRS.Open(strSQL,oConn);?
fAllowLogon?=?(oRS(0).Value?==?strName?&&?oRS(1).Value?==?strPwd)?
??true?:?false;?
如果SQL查詢沒有返回?cái)?shù)據(jù),程序?qū)⒂|發(fā)一個(gè)異常,隨后這個(gè)異常就被catch()捕獲。?
六、禁用父路徑?
確保文件名字中沒有出現(xiàn)“..”。按照如下步驟禁用父路徑:?
右擊Web網(wǎng)站的根,從菜單選擇“屬性”。?
●選擇“主目錄”選項(xiàng)卡。?
●點(diǎn)擊“配置”。?
●點(diǎn)擊“應(yīng)用程序選項(xiàng)”。?
●取消“啟用父路徑”。?
如果要從命令行禁用父路徑,請(qǐng)執(zhí)行如下命令:?
cscript....?adsutil.vbs?set?w3svc/1/root/AspEnableParentPaths?false?
要真正做到對(duì)輸入攻擊的全面防范,你必須有一切輸入數(shù)據(jù)都可能有危險(xiǎn)的心理準(zhǔn)備。檢查合法的輸入,而不是檢查不合法的輸入,因?yàn)?br />
攻擊者很快就可以找出突破不合法規(guī)則的辦法。同時(shí),學(xué)習(xí)并正確地運(yùn)用正則表達(dá)式。記住了這幾點(diǎn),你就能夠大大減少Web應(yīng)用被侵入的機(jī)會(huì)。?
posted on 2006-07-23 17:43 小澗流水 閱讀(242) 評(píng)論(0) 編輯 收藏 所屬分類: 信息安全