沙漠绿洲

----骆驼之行

 

转 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应用被侵入的机会。 

posted on 2006-07-23 17:43 小涧流水 阅读(230) 评论(0)  编辑  收藏 所属分类: 信息安全


只有注册用户登录后才能发表评论。


网站导航:
 

导航

统计

公告

Free Counter
Free Web

常用链接

留言簿(1)

随笔分类(43)

随笔档案(48)

文章档案(1)

收藏夹(12)

网络

计算机图形学

搜索

最新评论

阅读排行榜

评论排行榜