一、攻击者绕过身份验证的方法
设想有这样一个简单的用户登录程序:它通过表单获取用户名字和密码,然而把这些数据发送到服务器端,服务器端程序查找数据库并验
证用户身份。这种用户身份验证方法非常常见。检查用户名字和密码时,许多开发者使用的是类如下面的代码:
<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应用被侵入的机会。