|
防:
有攻击,就会有防守。针对上面的攻击,经过一段时间的测试并且努力编程,我实现我自己以前的一个小理想――我写出了自己的防暴力破解及限制单用户同时刻登陆一次等相关代码。
测试网址:http://www.gdsx.net/FinalLogon/ ,现在跟各位分享下我的编程成果。我写的这个登陆接口暂时没有认证码(各位可以自己根据自己的情况添加这个功能),但是我还是觉得安全性已经过得去拉。
总的来说有如下几个特点:
1、我不限制ip,但是每一个ip只能登陆5次(系统会自动记录每一个ip相应的登陆次数),每一个ip尝试登陆5次,如果还是没有成功登陆时,继续尝试系统均会提示“真遗憾,已经达到了登陆帐号的次数,请过五分钟后再试:(!”,就是说到5分钟后才可以再用这个帐号登陆(成功登陆之后会把数据库里的所有 ip地址记录及相应ip对应的记录器清空,否则就不清空相关记录);
2、如果你用3个以上的代理ip来尝试破解的话,系统也会把整个帐号锁定,但还是继续让你暴力破解,但是你却无法知道正确的帐号密码;
3、并且每一个帐号在登陆使用中的5分钟时段内只能登陆一次,意思是说假如有一个人用了你的帐号已经登陆成功了,在他登陆的期间,同一个帐号不允许同时在两个地方登陆,如果你也要登陆的话,除非你有超级管理员密码,你可以用超级管理员密码登陆第二套方案中的后台,把你帐号关联的密码修改掉,并且在你修改的这段时间,任何的同帐号(不是当前的你,而是正使用你的帐号登陆的其他人)的操作都被冻结(他们进行的任何操作都会马上被Redirect到一个需要输入超级管理员密码才能继续操作的登陆页面);
4、还可以防止本地提交非法数据;
5、md5加密密码。在此只介绍核心代码――防暴力破解及限制单用户同时刻登陆一次代码,其他的就不介绍了。为了代码了易读性、简洁性,我把代码功能实现模块话。
首先介绍下数据库的设计,如图3:
图3
注:为了设计方便,我用的是access数据库,设计了Manage_User主表后最好再设计一个nodown表,用来防止数据库被非法下载,数据库完成后改为#%23databsetw9c45_data.asp
接着介绍框架代码如下:
框架代码的思路主要是说,当客户端传递参数给服务器的时候,首先要禁止从站点外部提交数据(直接在地址栏上输入验证地址或者本地提交等方式),接着根据客户提交的帐号到数据库里查询,假如不存在则关闭数据库,并转向相应的页面,存在该帐号就检测该帐号是否被锁,如果没有被锁,就调用 Logon_NotLocked过程,被锁就判断是否超时(超时时间我设为5分钟,这个各位可以根据自己的情况来设定),如果超时就调用 Logon_Locked_Timeout过程,如果没有超时就调用Logon_Locked_NotTimeout过程,如图4.
图4
| 以下是引用片段: <% 框架代码看起来代码简洁明了,层次分明。强烈建议初学者掌握这种模块化思想。'............禁止从站点外部提交数据 server_v1=Cstr(Request.ServerVariables("HTTP_REFERER")) server_v2=Cstr(Request.ServerVariables("SERVER_NAME")) if mid(server_v1,8,len(server_v2))<>server_v2 then %> <% '......对恶意刺探者“恐吓”下。呵呵^.^ %> < SCRIPT>alert("哈哈。你中木马拉!");alert("哈哈。木马下载中!");alert("哈哈。我很快就可以控制你拉!"); alert("哈哈。现在你最好是重新安装系统吧!");alert("哈哈。跟你开玩笑拉^.^本站不允许外地提交数据!")< /SCRIPT> <% response.end end if '............禁止从站点外部提交数据语句结束 %> <% '........接着获取客户传递上来的参数:用户名跟密码 UserName=trim(Request.Form("name")) PassWord=trim(Request.Form("pwd")) '........调用CheckInfuse函数过滤非法字符(本文只讨论核心代码,所以这个函数在此就不公布了(如下的非核心函数全部不公布),各位可以上网找)。 Call CheckInfuse(UserName,35) Call CheckInfuse(PassWord,35) '........用MD5加密客户传递上来的密码值 PassWordmd5=md5(PassWord,16) set rs=server.createobject("adodb.recordset") sqltext="select * from Manage_User where UserName='" & UserName & "'" rs.open sqltext,conn,1,1 if rs.bof and rs.eof then '........查找数据库,检查用户名是否存在,假如不存在则关闭数据库RS,并转向相应的错误提示页面 Call Close_Data(rs) Response.Redirect "login.asp?msg=您输入了错误的帐号,请再次输入!" response.end else '........假如存在这个帐号,那么首先验证帐号是否已锁 '........ LogonTime这个变量用来记录数据库里的帐号上一次的登陆时间 '........LogonLock这个变量用来记录数据库里的帐号是否已锁 '........ LogonTimeNow这个变量用来记录服务器当前的时间 '........ LogonTimeCam这个变量用来记录LogonTime及LogonTimeNow两个变量的时间差,用来验证帐号锁定时间是否已过5分钟 LogonTime=rs("LogonTime") LogonLock=rs("LogonLock") LogonTimeNow=Now() LogonTimeCam=DateDiff("s",LogonTime,LogonTimeNow) if LogonLock = 1 then '........假如存在这个帐号,验证帐号是否已锁 假如被锁 if LogonTimeCam > 300 then '.......假如帐号已锁,验证帐号锁定时间是否已过5分钟,如已过5分钟,调用Logon_Locked_Timeout过程 Call Logon_Locked_Timeout() else '.......假如帐号已锁,验证帐号锁定时间是否已过5分钟,如还没有过5分钟,调用Logon_Locked_NotTimeout过程 Call Logon_Locked_NotTimeout() end if '.......假如帐号已锁,验证帐号锁定时间是否已过5分钟 语句结束 else '.......帐号没有被锁 ,调用Logon_NotLocked过程 Call Logon_NotLocked() end if '.......假如存在这个帐号,那么首先验证帐号是否已锁 语句结束 end if '查找数据库,检查用户名是否存在 语句结束 %> 细节代码如下: <% '........把查询语句模块化,主要是用来更新表中的信息 sub Select_Name(UserName) set rs=server.createobject("adodb.recordset") sqltext="select * from Manage_User where UserName='" & UserName & "'" rs.open sqltext,conn,1,3 end sub '.......下面这个过程主要的意思是:假如帐号已锁,并且已经超过5分钟,这个时候就验证客户的帐号密码是否都正确,如果正确那么就调用Logon_Other过程来验证某个帐号现在是否在其他地方登陆,如果不正确就关闭数据库RS并转向相应的错误提示页面。 sub Logon_Locked_Timeout() if rs.recordcount >= 1 and rs("UserName")=UserName and rs("PassWord")=PassWordmd5 then '.......验证帐号密码是否合法,假如合法的话 LogonedIpAddress=rs("LogonedIpAddress") '.......验证某个帐号现在是否在其他地方登陆 Call If_Logon_Other(LogonedIpAddress) '........验证某个帐号现在是否在其他地方登陆 语句结束 else '.......假如帐号已锁,验证帐号锁定时间是否已过5分钟,如已过5分钟,但验证帐号密码不合法的情况 Call Close_Data(rs) Response.Redirect "login.asp?msg=您帐号已经被系统锁定,可能是因为有恶意用户在暴力破解您的帐号,请跟系统管理员联系!" end if '.......假如帐号已锁,验证帐号锁定时间是否已过5分钟,如已过5分钟。验证帐号密码是否合法 语句结束 end sub '....... 下面这个过程主要的意思是:假如帐号已锁,并且还没有超过5分钟,这个时候就验证客户的帐号密码是否都正确,如果正确那么首先验证客户的ip地址及其相关的登陆次数是否达到5次,如果客户的ip地址及其相关的登陆次数还没有达到5次的话就调用 If_Logon_Other_Locked过程来验证某个帐号现在是否在其他地方登陆,如果不正确就关闭数据库RS并转向相应的错误提示页面。 sub Logon_Locked_NotTimeout() if rs.recordcount >= 1 and rs("UserName")=UserName and rs("PassWord")=PassWordmd5 then '.......假如帐号已锁,验证帐号锁定时间是否已过5分钟,如还没有过5分钟 验证帐号密码是否合法,假如合法的话 '......首先验证客户的ip地址及其相关的登陆次数是否达到5次 Call First_Ip_Note_Check() LogonedIpAddress=rs("LogonedIpAddress") '.......验证某个帐号现在是否在其他地方登陆 Call If_Logon_Other_Locked(LogonedIpAddress) '........验证某个帐号现在是否在其他地方登陆 语句结束 else '.......假如帐号已锁,验证帐号锁定时间是否已过5分钟,如没过5分钟,并且验证帐号密码不合法的情况 Call Close_Data(rs) Response.Redirect "login.asp?msg=您帐号已经被系统锁定,可能是因为有恶意用户在暴力破解您的帐号,请跟系统管理员联系!" end if '.......假如帐号已锁,验证帐号锁定时间是否已过5分钟,如还没有过5分钟 验证帐号密码是否合法 语句结束 end sub '....... 下面这个过程主要的意思是:假如帐号已锁,并且已经超过5分钟及客户的帐号密码都正确,如果LogonedIpAddress不为空,则表明当前已经有人登陆了,因为这种情况可能是之前意外操作造成的(比如意外关机等),所以就把用户Redirect到一个页面,给于提示“您帐号现在已经在其他地方登陆,如果不是您之前意外操作造成的(比如意外关机等),请用超级管理员密码登陆修改密码。如果您觉得安全的话,可以直接<a href =MoveTo.asp>登陆后台</a>”,如果你觉得你的帐号已经被他人登陆了,可以马上用超级管理员密码登陆第二套方案中的后台,把你帐号关联的密码修改掉,并且在你修改的这段时间,任何的同帐号(不是当前的你,而是正使用你的帐号登陆的其他人)的操作都被冻结(他们进行的任何操作都会马上被Redirect到一个需要输入超级管理员密码才能继续操作的登陆页面),这样做的好处就是可以避免类似QQ软件的“速度比赛”的怪现象 ――假如有两个人在两个地方登陆,那就是一个比速度的问题(比谁修改密码的速度快);如果LogonedIpAddress为空,那么表明当前帐号现在没有在其他地方登陆,是安全的,就更新数据库相应的信息后直接登陆后台。 sub If_Logon_Other(LogonedIpAddress) if LogonedIpAddress <>"" then session("breachadminoked")="breachadminok" session("UserName")=rs("UserName") Call Close_Data(rs) Response.Redirect "LoginTo.asp?msg=您帐号现在已经在其他地方登陆,如果不是您之前意外操作造成的(比如意外关机等),请用超级管理员密码登陆修改密码。如果您觉得安全的话,可以直接<a href =MoveTo.asp>登陆后台</a>" else '.......某个帐号现在不在其他地方登陆的情况 Call Close_Data(rs) UserName=trim(Request.Form("name")) Call CheckInfuse(UserName,35) Call Select_Name(UserName) rs("IpAddress")="127.0.0.1" rs("LogonNumber")="1" rs("LogonLock")=0 rs("LogonedIpAddress")=Request.ServerVariables("REMOTE_ADDR") rs("LogonedTime")=rs("LogonTime") rs("LogonTime")=Now() rs.update session("breachadminoked")="breachadminok" session("UserName")=rs("UserName") Call Close_Data(rs) Response.Redirect "main.asp" '.......某个帐号现在不在其他地方登陆的情况 语句结束 end if end sub '....... 下面这个过程主要的意思是:假如帐号已锁,并且还没有超过5分钟,帐号密码也都正确,如果现在不在其他地方登陆,则 Redirect到一个跟输入错误帐号密码差不多的登陆页面,错误提示信息是“您帐号已经被系统锁定,可能是因为有恶意用户在暴力破解您的帐号,请跟系统管理员联系!!”跟输入错误帐号密码的提示信息(您帐号已经被系统锁定,可能是因为有恶意用户在暴力破解您的帐号,请跟系统管理员联系!)只差一个字符 “!”。其实这样设计有两个用意:一是为了防止暴力破解特意设置的,这么细微的地方,我想没有几个人会注意到(除了系统管理员外),二是我们可以在这个特定的页面搞点特殊的东西,比如只有在这种情况才显示出超级管理员登陆接口的地址(防止管理员遗忘第二套后台登陆接口的地址),并且这个地址要隐藏起来,就跟背景颜色一样算了,只有用鼠标一移才可以看到第二套后台操作的登陆地址,管理员看到这样的页面就知道有人在破解后台帐号密码,并且也不会因为帐号已经被系统锁定而非要等到5分钟后才登陆。如图5.(在下面) 如果现在已经有人登陆了后台,那么就不能设计上面的那种情况了,可以把用户 Redirect到一个页面,给于提示“您帐号现在已经在其他地方登陆,如果不是您之前意外操作造成的(比如意外关机等),请用超级管理员密码登陆修改密码。如果您觉得安全的话,可以直接<a href =MoveTo.asp>登陆后台</a>”,如果你觉得你的帐号已经被他人登陆了,可以马上用超级管理员密码登陆第二套方案中的后台,把你帐号关联的密码修改掉,并且在你修改的这段时间,任何的同帐号(不是当前的你,而是正使用你的帐号登陆的其他人)的操作都被冻结(他们进行的任何操作都会马上被Redirect到一个需要输入超级管理员密码才能继续操作的登陆页面)。 sub If_Logon_Other_Locked(LogonedIpAddress) if LogonedIpAddress <>"" then session("breachadminoked")="breachadminok" session("UserName")=rs("UserName") Call Close_Data(rs) Response.Redirect "LoginTo.asp?msg=您帐号现在已经在其他地方登陆,如果不是您之前意外操作造成的(比如意外关机等),请用超级管理员密码登陆修改密码。如果您觉得安全的话,可以直接<a href =MoveTo.asp>登陆后台</a>" else '.......某个帐号现在不在其他地方登陆的情况 session("Sup_Logon_2_1")="Sup_Logon_2_1" session("UserName")=rs("UserName") Call Close_Data(rs) Response.Redirect "login.asp?msg=您帐号已经被系统锁定,可能是因为有恶意用户在暴力破解您的帐号,请跟系统管理员联系!!" '.......某个帐号现在不在其他地方登陆的情况 语句结束 end if end sub '....... 下面这个过程主要的意思是:帐号没有被锁,验证帐号密码是否正确的情况, 假如正确, 那么首先调用First_Ip_Note_Check过程验证客户的ip地址及其相关的登陆次数是否达到5次,假如还是没有达到登陆极限,那么调用 If_Logon_Other过程验证该帐号现在是否在其他地方登陆;假如帐号密码不正确,那么首先调用Ip_Note_Timeout过程判断登陆时间是否已过5分钟,如果已过5分钟,,那么就清空之前的所有的ip记录集,接着调用Ip_Note过程记录客户的尝试登陆IP地址,如果帐号密码还是不正确就关闭数据库RS并转向相应的错误提示页面。 sub Logon_NotLocked() if rs.recordcount >= 1 and rs("UserName")=UserName and rs("PassWord")=PassWordmd5 then '.......帐号没有被锁的情况,验证帐号密码是否正确的情况 假如正确的情况 '......首先验证客户的ip地址及其相关的登陆次数是否达到5次 Call First_Ip_Note_Check() LogonedIpAddress=rs("LogonedIpAddress") '.......帐号没有被锁的情况,假如帐号密码正确的情况,首先验证某个帐号现在是否在其他地方登陆 Call If_Logon_Other(LogonedIpAddress) '........帐号没有被锁的情况,假如帐号密码正确的情况,验证某个帐号现在是否在其他地方登陆 语句结束 else '.......帐号没有被锁的情况,验证帐号密码不合法的情况 假如不正确的情况 LogonTime=rs("LogonTime") LogonTimeNow=Now() LogonTimeCam=DateDiff("s",LogonTime,LogonTimeNow) '.......判断登陆时间是否已过5分钟 Call Ip_Note_Timeout(LogonTimeCam) '.......假如登陆时间已过5分钟 语句结束 Call Close_Data(rs) UserName=trim(Request.Form("name")) Call CheckInfuse(UserName,35) Call Select_Name(UserName) '.......记录客户的尝试登陆IP地址 Call Ip_Note() Call Close_Data(rs) Response.Redirect "login.asp?msg=您输入了错误的帐号或口令,请再次输入!"+msg end if '.......帐号没有被锁的情况,验证帐号密码是否正确的情况 语句结束 end sub '......因为下面的代码里已经有了很多的注释帮助理解,所以我就不重复罗嗦拉。 sub First_Ip_Note_Check() '......首先验证客户的ip地址及其相关的登陆次数 IpAddress=rs("IpAddress") names=Split(IpAddress,"@@") i=0 for each name in names '...........数据库已经存在客户正登陆的ip if Request.ServerVariables("REMOTE_ADDR") = names(i) then 'msg="数据库已经存在这个ip" '...........验证登陆次数 LogonNumber = rs("LogonNumber") LogonNumberNames=Split(LogonNumber,"@@") P=0 for each LogonNumberName1 in LogonNumberNames if P = i then '判断某个ip所对应的次数记录集,如果相等的情况 if LogonNumberNames(P) >= "5" then '判断某个ip所对应的次数记录集,如果某个ip所对应的次数记录集为大于等于5 msg="真遗憾,已经达到了登陆帐号的次数,请过五分钟后再试:(!!" session("Sup_Logon_2_1")="Sup_Logon_2_1" session("UserName")=rs("UserName") Call Close_Data(rs) Response.Redirect "login.asp?msg=您输入了错误的帐号或口令,请再次输入!"+msg Response.End() end if '判断某个ip所对应的次数记录集,如果某个ip所对应的次数记录集为5 语句结束 end if '判断某个ip所对应的次数记录集,如果相等的情况 语句结束 P = P +1 next end if '判断数据库里是否已经存在这个ip 语句结束 i=i+1 next end sub sub Ip_Note_Timeout(LogonTimeCam) if LogonTimeCam > 300 then '.......假如登陆时间已过5分钟,那么就清空所有的ip记录集 Call Close_Data(rs) UserName=trim(Request.Form("name")) Call CheckInfuse(UserName,35) Call Select_Name(UserName) rs("IpAddress")="127.0.0.1" rs("LogonNumber")="1" rs("LogonTime")=Now() rs.update end if end sub sub Ip_Note() '......记录客户的ip地址及其相关的登陆次数 attrib=rs("IpAddress") names=Split(attrib,"@@") y=0 for each name in names y=y+1 next 'msg=cstr(y) IpAddress=rs("IpAddress") names=Split(IpAddress,"@@") i=0 for each name in names 'msg=msg+names(i)+"**" '...........数据库已经存在客户正登陆的ip if Request.ServerVariables("REMOTE_ADDR") = names(i) then 'msg="数据库已经存在这个ip" '...........验证登陆次数 LogonNumber = rs("LogonNumber") LogonNumberNames=Split(LogonNumber,"@@") P=0 for each LogonNumberName1 in LogonNumberNames '判断某个ip所对应的次数记录集,把之前的次数用一个临时变量保存起来 if P < i then TemMsg=TemMsg+LogonNumberNames(P)+"@@" end if '判断某个ip所对应的次数记录集,把之前的次数用一个临时变量保存起来 语句结束 if P = i then '判断某个ip所对应的次数记录集,如果相等的情况 if LogonNumberNames(P) >= "5" then '判断某个ip所对应的次数记录集,如果某个ip所对应的次数记录集为大于等于5 msg="真遗憾,已经达到了登陆帐号的次数,请过五分钟后再试:(!" '如果所有的ip数量是3个的时候,就就锁定帐号 if y >= 3 then rs("LogonLock")=1 rs.update end if '如果所有的ip数量是3个的时候,就就锁定帐号 语句结束 exit for else '............同样一个ip登陆次数没有达到极限 if P = y-1 then TemMsg=TemMsg+cstr(cint(LogonNumberNames(P))+1) else TemMsg=TemMsg+cstr(cint(LogonNumberNames(P))+1)+"@@" pp=P for pp=P+1 to y-2 TemMsg=TemMsg+LogonNumberNames(pp)+"@@" pp=pp+1 next TemMsg=TemMsg+LogonNumberNames(y-1) end if '............同样一个ip登陆次数没有达到极限 语句结束 rs("LogonNumber")=TemMsg rs("LogonTime")=Now() rs.update end if '判断某个ip所对应的次数记录集,如果某个ip所对应的次数记录集为5 语句结束 end if '判断某个ip所对应的次数记录集,如果相等的情况 语句结束 P = P +1 next '...........验证登陆次数语句结束 '假如数据库里已经存在这个ip的话,就不再往数据库里写入这个ip exit for elseif Request.ServerVariables("REMOTE_ADDR") <> names(i) and i = y-1 then '...........数据库不存在客户正登陆的ip rs("IpAddress")=rs("IpAddress")+"@@"+Request.ServerVariables("REMOTE_ADDR") rs("LogonNumber")=rs("LogonNumber")+"@@"+"1" rs("LogonTime")=Now() rs.update end if '判断数据库里是否已经存在这个ip 语句结束 i=i+1 next end sub '关闭数据库RS function Close_Data(data) data.close set data=nothing end function %> |
| 新云CMS Online.asp页面过滤不严 | 02-26 |
| 对网软网上购物系统的漏洞分析 | 01-09 |
| 测试SQL防注入脚本 | 12-21 |
| Google Xss又出跨站新漏洞 | 11-06 |
| 一次简单的html injection导致的 | 11-06 |
| 风讯、科讯漏洞利用 | 11-01 |
| Adobe pdf reader URI利用方式浅 | 10-23 |
| 超星阅览器的最新0DAY | 10-19 |
| 运用SQL Injection做数据库渗透的 | 09-22 |
| sa-blog 0day | 09-22 |
| HTML注入的一些简单想法 | 09-10 |
| 网站登陆接口的攻与防 | 09-04 |