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