1.关于交互式动态网页可能存在的问题 1.1 form类型的交互 1.1.1 概念介绍 在我们和浏览者进行交互时,最常用到的就是form(post/get/put方法),虽然非常方便,但是很多问题也是因他而起。 form表单中input标志 用来接受用户输入的信息,例如:用户名、密码、email等。如果你没有对用户输入进行很好的检查的话,一个恶意的用户会屏蔽掉一些安全机制,绕过安全认证。例如,输入标准的HTML语句或者javascript语句会改变输出结果 ,在输入框中打入标准的HTML语句会得到什么样的结果呢?比如一个留言本,我们留言内容中打入:<font size=10>你好!</font> 如果你的程序中没有屏蔽html语句,那么就会改变"你好"字体的大小。在留言本中改变字体大小和贴图有时并不是什么坏事,反而可以使留言本生动。但是如果在输入框中写个 Javascript 的死循环,比如: <a herf="http://someurl" onMouSEOver="while(1) {window.close('/')}">第一万个惊心动魄</a> 那么其他查看该留言的客人只要移 动鼠标到"第一万个惊心动魄",上就会使用户的浏览器因死循环而死掉。 1.1.2 防范要点 (1)对特殊字符进行过滤 ([/&;/`'///|"*?~<>^/(/)/[/]/{/}/$/n/r])///$1/g;),这个是最基本的,在很多地方也已经不只一次提到过 <script language="vbscript"> sub uBotton_onclick if form1.uUserName.value=""then msgbox"您的姓名不能为空!",0+32,"哦!还不行" form1.uUserName.focus exit sub end if
if form1.uPassWord.value=""then msgbox"您的密码不能为空!",0+32,"哦!还不行" form1.uPassword.focus exit sub end if
if form1.uUserName.value=""then msgbox"您的姓名不能为空!",0+32,"哦!还不行" form1.uUserName.focus exit sub end if form1.submit end sub </script>
function isEmpty(objname) { var str = document.inputform[objname].value var tmpstr = str.replace([/&;/`'///|"*?~<>^/(/)/[/]/{/}/$/n/r])///$1/g;,"") var tmpstr = tmpstr.replace([/&;/`'///|"*?~<>^/(/)/[/]/{/}/$/n/r])///$1/g;,"") return (tmpstr.length==0) }
function check() { tf=document.inputform errors="" if (isEmpty("username")) errors += "用户名不能为空。/n"; if (isEmpty("password")) errors += "密码不能为空!/n" if (errors!="") alert(errors); return (errors=="") } (2) 对输入的字符长度进行限制 (3) 进行尽可能多的错误出理和错误陷阱 (4) 尽可能多的使用以下这些标志,减少用户输入的机会 <input type="checkbox" name="checkbox" value="checkbox"> <select name="select"> </select> <input type="radio" name="radiobutton" value="radiobutton">
<!--#include file="conn.asp"--> <% dim errmsg if request.form("username")="" then ErrMsg="用户名不能为空" foundError=True else UserName=request.form("UserName") end if
if request.form("password")="" then ErrMsg="密码不能为空" foundError=True else PassWord=request.form("PassWord") end if if FoundError=true then showAnnounce(ErrMsg) else set rstmp=server.createobject("adodb.recordset") if Request.ServerVariables("REQUEST_METHOD") = "POST" then rstmp.open "Select * from User Where userName='" & UserName & "'",conn,3,3 if rstmp.bof then session.contents("UserName")=UserName rstmp.addnew rstmp("username")=username rstmp("userpassword")=password rstmp("logins")=1 rstmp("online")=1 rstmp.update response.redirect("index.asp") elseif PassWord<>rstmp("userpassword") then ErrMsg="密码错啦" foundError=True showAnnounce(ErrMsg) else session.contents("UserName")=UserName rstmp("logins")=rstmp("logins")+1 rstmp("online")=1 rstmp.update rstmp.close Set rstmp=nothing response.redirect("index.asp") end if
else if session.contents("UserName")<>"" then rstmp.open "Select * from User Where userName='"&session.contents("UserName")&"'",conn,3,3 rstmp("logins")=rstmp("logins")+1 rstmp("online")=1 rstmp.update rstmp.close Set rstmp=nothing conn.close set conn=nothing response.redirect("index.asp") end if end if end if %> <html>
if adname="" then response.redirect "login.asp" end if if passwd="" then response.redirect "login.asp" end if
if adname="focus-admin" and passwd="1" then response.redirect "manage.asp" else response.redirect "login.asp" end if %> ###---checklogin.asp----end ###---manage.asp <%
dim where dim where1 dim refererURL dim refererURL2 dim refererURL3 refererURL=phyURL&"login.as" refererURL2=phyURL&"edit.asp" refererURL3=phyURL&"manage.a" refererURL4=phyURL&"savearti" where=Request.ServerVariables("HTTP_REFERER") where=left(where,(len(phyURL)+8)) if where<>refererURL and where<> refererURL2 and where<>refererURL3 and where<>refererURL4 then Response.Redirect "login.asp" end if
const MaxPerPage=20 dim totalPut dim CurrentPage dim TotalPages dim i,j
if not isempty(request("page")) then currentPage=cint(request("page")) else currentPage=1 end if
I. setcookie内容必须完整包含帐号密码,或类似的完整安全信息,如果只携带帐号信息或用某种权限标志来认证,极容易造成非法入侵。 例如某站点中的会员更新页面中携带的认证信息是两个,用户名和Uid(均为明文传送)已知Uid对于每个会员是唯一的。由于我们只需要知道对方的帐号和Uid就可以更改对方信息(不需要知道密码!),只要攻击者知道Uid(攻击者可以通过暴力猜测的方法来得到Uid,有时候站点本身也会泄露用户的Uid,例如在论坛等处)那么,攻击者就可以通过遍历攻击完成对任意一个帐号的信息更改。
II. 必须所有需要权限操作的页面都必须执行认证判断的操作。如果任何一页没有进行这种认证判断,都有可能给攻击者以恶意入侵的机会。
III. 很多网站为了方便,将用户名以及口令信息储存在Cookie中,有的甚至以明文方式保存口令。如果攻击者可以访问到用户的主机,就可能通过保存的Cookie文件得到用户名和口令。
4 .实例说明 下面这个例子是在网上经常被提到的,这是个非常经典的例子,所以在这里通过这个实例告诉大家可能存在的危险。问题描述: 大部分网站把密码放到数据库中,在登陆验证中用以下sql,(以asp为例) sql="select * from user where username='"&username&"'and pass='"& pass &'" 此时,您只要根据sql构造一个特殊的用户名和密码,如:ben' or '1'='1 就可以进入本来你没有特权的页面。再来看看上面那个语句吧: sql="select * from user where username='"&username&"'and pass='"& pass&'" 此时,您只要根据sql构造一个特殊的用户名和密码,如:ben' or '1'='1 这样,程序将会变成这样: sql="select*from username where username="&ben'or'1'=1&"and pass="&pass&" or 是一个逻辑运算符,作用是在判断两个条件的时候,只要其中一个条件成立,那么等式将会成立.而在语言中,是以1来代表真的(成立).那么在这行语句中,原语句的"and"验证将不再继续,而因为"1=1"和"or"令语句返回为真值.。
另外我们也可以构造以下的用户名: username='aa' or username<>'aa' pass='aa' or pass<>'aa' 相应的在浏览器端的用户名框内写入:aa' or username<>'aa 口令框内写入:aa' or pass<>'aa,注意这两个字符串 两头是没有'的。这 样就可以成功的骗过系统而进入。
具体实施是这样的,首先我会到注册的地方去收集信息,了解尽可能多的信息,例如目标数据库中都有用户的什么样的信息,随便的填写信息然后提交,当你要注册的用户名被注册的是有系统会提示你已被注册,有的网站做的更好的,就是他们专门给你设置的检测是否有已经被注册的功能,通过这样就会非常容易的找到目标--那个提示已被注册的用户,让后你在这个注册页里填写一些特殊的字符,如',/,,等字符看系统如何提示,以证明程序员是否注意到了应该过滤字符或懂得是否应该过滤那些字符,在这页进行尝试是因为有的网站在登录的时候他会记录你的ip地址,当然你也可以找一个比你直接登录要快的代理服务器来做跳板。后面你要做的就是察看登录页的html源代码,看看是否有在客户端的字符过滤,看看这个程序员是用什么风格来编写程序,尽可能多的了解程序编写风格,这对你以后的某些判断有好处。如果有在客户端的过滤也不怕,你要搞清是什么样的过滤,能不能对攻击造成威胁,不要一看有过滤就害怕,可以尝试着用别的方法绕,就是使用自己精心打造的独立脚本,进行攻击。然后你要看看form的action中的url是否可以直接提交,在浏览器地址栏里直接提交,看看返回什么,是否有来路检测。还有很多细小的地方,你也应该可以注意到,例如那些地方程序员的整体的编写风格是什么,变量名定义的风格是什么等等,这个会帮我们"猜"到很多东西。还有别的其他什么,我也记不太清楚了,临场发挥吧。通过这些了解我们有如下几种可能: 1.那个程序员非常善良相信全世界都是好人,什么都没做,根本没有任何检测机制,我们直接用username='aa' or username<>'aa', pass='aa' or pass<>'aa'就可以搞定,现在这么善良的人少啦,可是你要是有耐心,找到这种人还是不难的。 2.这个程序员可能听别人提起过一些安全问题,毕竟现在这个那里都有人说,很多书中都有提及,但是做得不够好,他只进行了简单的输入过滤。 过滤有两种方式,一种是在客户端的过滤,一种是在服务器端的过滤。现在很多的程序员考虑到再服务器端进行过滤可能给服务器造成更多的负荷,会把检测过程放在客户端。如果他在服务器端没做任何事情,那么还是可以对其进行攻击的,我可以将这个登录页的源代码COPY下来,然后自己建立一个文件把这些代码PASTE进去,再对这个文件进行进一步的深加工,去掉原来页的过滤机制,或者直接将攻击代码写到这个文件中去,然后将form中的action中的地址改成绝对地址,也就是将文件名改成"http://www.target.com/targer.php"这样,然后就可 以提交啦。但是如 果服务器端加上了"来路检测",你就白玩了。如果这样还是不行,我再换一种方法,在浏览器的地址栏里用?来输入参数,就好像 "http://www.targer.com/targer.php?username='aa' or username<>'aa'&pass='aa' or pass<>'aa' "然后敲回车吧,其 实应该先 尝试这种方法因为这用方法更简单,防护起来也很简单,这种提交方式不是post 而是get ,只要服务器端程序检测你的提交方法,就可以kill掉这 个阴谋。如果单纯的只检测了"来路",还是不太安全的,可以先正确的提交一次,在提交过程中马上停止,就是保存这个环境,然后再构造请求。 (我做过几次试验得到的结果都不太一样,应该是和终止的时机有关,欢迎大家来交流)。 3.一个很出色的程序员,安全意识非常高,他在服务器端做了如下检测:检测提交的方法;检测提交的"来路";检测提交内容的长度;全面检测提交内容,这样我们就很难通过上面的方法对其进行攻击,那到保密的资料就已经不太可能了(如果各位还有什么好的办法,请一定来信告诉小弟,小弟在这里先谢了)。但是我还想说的是攻击并不代表是非要入侵进去,拿到某些东西才叫入侵,对你的机器进行破坏也叫入侵啊,例如提交一些错误的请求,脚本解释程序就会非常规矩的给你返回错误信息,最浅显的后果就是暴露物理路经,有的时候一些特殊的请求会使web服务宕掉,这些个我认为绝对是属于攻击,绝对是危害,也许你认为暴露物理路径没有什么,是在单独看来没有什么,但要是在一个有计划的攻击里,这个就会发挥很多作用,那时你可能还会莫名为什么他们找到了我的文件呢。也许有人认为这个是脚本解释程序的bug,也许有的是,但是返回错误信息绝对不是脚本解释程序的错误,这个是每个解释程序都要做到的,在我看来这个应该是还是程序员的问题,程序员没有做好对错误的处理。每一本教你如何编写程序的书籍里基本都会有错误处理之类的章节,并且每种语言基本都有错误处理函数和方法,只不过你没有想到罢了。至于究竟要怎么处理那就要看你对cgi程序安全的熟悉程度了,那就要看你对这种脚本语言的特性熟悉多少了,说到底就是经验,唯一的办法就是多看多写多想多交流。 4.非常优秀的程序员,以上那些做的都非常好(也许就是你啊,毕竟不难嘛,加上很少的代码就可以了),怎么办??怎么办?!怎么办!!在一旁偷偷的佩服吧!哈哈。