[讨论]用'or''=''or'就能登陆网
议题提交:小行信息来源:邪恶八进制信息安全团队
现在很多的公司网站的后台漏洞是大的叫人害怕
只要写入'or' '=',
就可以顺利的登入它的后台管理
针对于此,大家都是如和处理的?
希望各位发出自己的检验,完善下这个帖子,
PS:为安全做点力量
[b]我本着抛砖引玉思想欢迎大家来一起讨论此帖,谢谢![/b]
此帖已经在远望论坛发帖子讨论,已经有人回复总结一文,在此一起发出,希望更多的人针对此发表自己的见解
针对ASP中SQL Injection的初级防护(总结版)
robur/文
记得大约是在2002年的时候,SQL Injection(SQL注入)开始有了最初的雏形。黑客们发现,在动态网页的某些地方(表单等……)插入一些可以在数据库查询中起作用的语句和符号,就可以让页面执行后,返回特殊的结果。于是,经过了2003的发展,到2004年,SQL Injection已经作为一种很成熟的入侵技术登上了安全界的舞台。也是2004年,有大批的网站程序被发现了SQL Injection漏洞,也有大批的网站栽倒在这种攻击手段之下。
下面,我就[color=seagreen]小行[/color]和[color=green]逍遥一指令[/color]的帖子,作总结性的说明,仅供参考。
正如如小行所说的,在某些网站的登录界面,如果你知道了存在的用户名(例如:admin),你无须知道密码。只需在密码框中输入' or ''='即可。(注意,全是单引号!)这样在不知道密码的情况下,也可以用admin的身份登录。
但是,我想说的是,' or ''='在一些地方会出现问题。有些数据库不会认为“空”=“空”是成立的条件,这样就不行了。最好写成' or '1'='1
这样,SQL的查询语句,就变成了如下的样子。
select * from users where name='admin' and pass='123456' or '1'='1'
上面,蓝色的部分是用户在密码框中输入的数据。大家可以看见,由于用户的巧妙构造,使得SQL Server把这条查询语句解析为3个条件:
1、name='admin'
2、pass='123456'
3、'1'='1'
关键是中间的那个or,这样一来,SQL Server在解析的时候,只要条件2、3,符合一个就可以了。
因为1=1是衡成立的,所以这个查询的结果肯定是true。
既然已经知道了原理,那我们可以试着防范这种攻击。
我们的根本目标,就是把用户输入的单引号和or过滤掉,这样就能在基本上杜绝SQL Injection攻击。但是,需要强调的是,SQL语句中,起作用的还有and,分号,--(两个减号)等等。
解释一下:and用于连接两个条件。;(分号)用于分隔两个语句。(如果把两个语句写在一行,就需要它。)--(双减号只在MS SQL Server中起作用,含义是忽略后面的内容。这个通常在繁琐的MS SQL注入中使用。)
回归主题。asp中为我们提供的字符转义函数:Replace。看代码:
<%
Dim Usr,Pwd
Usr=Request.form("username") '从表单获得数据
Pwd=Request.form("password") '同上
if (Usr<>"") and (Pwd<>"") then '判断数据为非空
Usr=Replace(Replace(Usr,"'",""),"or","") '嵌套了两个replace函数,过滤特殊字符
Pwd=Replace(Replace(Pwd,"'",""),"or","")
end if
%>
Repalce语法规则:
Replace(变量名称,"要过滤的字符","要替换的字符")
说明,“要替换的字符”那里,如果留空,就是把要过滤的字符直接删除。
例子:
Replace(Pwd,"'or","")
过滤Pwd变量中的or字符。
由于Replace函数,一次只能对一组字符进行转义,所以讲讲Replace的嵌套:
Replace(Replace(Pwd,"or",""),"'","")
代码中红色的地方,被外面的replace函数看作一个变量,依此类推。
如果你还想过滤and,分号,--什么的,请看下面(完全过滤了在SQL注入中最关键的5个字符串。)
Replace(Replace(Replace(Replace(Replace(变量名称,"'",""),"or",""),"and",""),"--",""),";","")
上面只是防范从表单提交的数据,对于一些新闻系统,经常会出现诸如:
[url]http://host/news/show.asp?id=1[/url]
之类的URL。
如果在那个1后面,加上一个单引号,一样会报错。我们往下看。
如果加上了单引号,SQL语句就变成了:
select * from news where id='1''
因为最后一个引号没有闭合,所以系统一定会报错。
防范如下(其实和上面的原理是一样D……)看代码:
<%
Dim id
id=Request("id")
if Not IsNumeric(id) then '------事先声明了变量,所以可以直接写进去。否则,就要写Request("id")
Response.write "Error!" '------反馈错误信息
Response.end '------中止执行下面的语句
end if
%>
因为,类似新闻ID之类的变量,是根本不需要有字母和符号存在的,所以用IsNumeric来判断是否为纯数字,确保万无一失。
如果一个变量,里面需要有字母和符号,那你就只好用Replace函数慢慢过滤了……:O)
最后,再说两种防止ASP页面暴出错误信息的方法。
众所周知,入侵者往往都是通过页面暴出的错误来得知一些额外的信息。比如你的系统,SQL Server的版本,是否存在某数据库,SQL语句是否执行成功等……
防止入侵者看到错误信息,就是让入侵者变成了睁眼瞎,找不到头绪。(骨灰级黑客除外~~~~)
方法1:打开你的IIS管理器(以IIS6.0为例),选择你的网站=》属性页面=》主目录选项卡=》点击配置按钮=》调试选项卡=》向客户端发送下列文本错误消息
这样,即使入侵者找到了注入点,注入后,得到也只是服务器返回的固定的错误消息。不能得到任何有价值的错误信息。
方法2:在你的ASP语句中(一般是在最开头的地方),加上一句
On Error Resume Next
(注意,这句必须加在<% %>里面,不然IIS可不认。)
这就是著名的容错语句,当ASP语句执行时发生错误,就跳过错误,继续向下执行,而且不会返回错误信息。
当然,入侵者看不到错误信息,调试人员也看不到。所以,你一定在把一个ASP程序调试完毕后,再把这个语句加到ASP语句的顶端。否则,调试的时候,会烦死你的~~~~~~:O)
好了,不多说。就这些了,全是自己写的。大家随便看看,捧捧场哦~~~~~~
[b]就这些,期待审核通过,大家都能好好的谈论下这个问题[/b] <%
user=request.form("userID")
pass=request.form("password")
for i=l to len(userID)
cl=mid(userID,i,l)
if cl="'"or us="="or" us="<"or us=">" then
response.redirect"提交非法字符,IP已经记录"
response.end
end if
next
%> <%
user=request.form("userID")
if instr(user,"'")<>0 then
response.redirect "提交非法字符,IP已经记录"
response.end
end if
%> response.redirect"提交非法字符,IP已经记录"不对吧应该是:
response.write"提交非法字符,IP已经记录" 最简单的是用instr函数
这个东西好呀!instr函数
pwd = request.form("pwd")
name = request.form("name")
if instr(name,"'")<>0 or instr(pwd,"'")<>0 then
Response.Redirect "Loginsb.asp?msg=用户名/密码错误,请重新输入!"
response.end
end if
还有就是replace
adminname=trim(replace(request("adminname"),"'",""))
adminpwd=trim(replace(request("adminpwd"),"'",""))
当然可以过滤更多的了,着就看自己的了 如果是asp+access结构的话.
过不过滤无所谓.
asp+access密码验证可以这样写
...
.....
sql="select * from ad where user='"&user&"'"
rs.open sql,conn,1,1
if password=rs("password") then
成功登陆!
end if 要有效的防注入我想还是用通用防注入那样的方法最简单,直接获取传递过来的每个参数,进行检查,把关键的字符进行检查......非法就reponse.end下. 没想到小行居然也跑到这里来了,还把我们的讨论贴给发上来了
我觉得还是直接过滤 ’ 比较实际
Username=Replace(Request.form("username") ,"'","")
Password=Replace(Request.form("password") ,"'","")
直接在密码验证页面中这样写就可以了~! [quote][b]下面是引用haicao于06-12-2005 13:19发表的:[/b]
如果是asp+access结构的话.
过不过滤无所谓.
asp+access密码验证可以这样写
...
.....
.......[/quote]
sql="select * from ad where user='"&user&"'"
rs.open sql,conn,1,1
if password=rs("password") then
成功登陆!
end if
user字段俺这么输:
admin' or password='123456
如果运气好,有个密码正好是123456的
在密码字段再输个 123456
不就进去了么?
对于字符输入,还是过滤的比较好,至少单引号必须过滤
这样一来,被单引号引起来的东西,无论如何躲不过去了
比如'&user&'
把单引号转换为& #39;
只要user字段输任何东西,都会当作字符串处理,而单引号一出现又被替换掉
那么该字段的注射就不可能了(愚见,请大家批评)
所以事实上很多的注射都是发生在数字型参数里,因为程序员总是记得对于数字型的参数,在SQL里是不需要用单引号引起来的,那么,即使进行了过滤,如果不充分,还是可以进行注射的.
比如在BBSXP上次的blog.asp中,有个id字段进行了过滤,但是,我们完全可以绕过去.
[code]if id<>"" then
sql="select * from [calendar] where id="&id&" order by id Desc"[/code]
虽然这个id变量过滤掉了'
不过对于AC的我们构造一个union查询
12 union select 1,2,username,userpass,5,6,7 from [user] where membercode=5
就可以查询区长的用户名和密码了,当然,修改条件查询任何人的密码也没问题的
对于MSSQL,union会出错,那么我们这么做:
12 and exists(select * from clubconfig where substring(adminpassword,1,1) between 0x30 and 0x46)
这样就跳过了单引号的限制,还有比较大小时用到的>和<符号
用二分法很容易得到所有数据,毕竟这种公开代码的程序的表的结构都很清楚
总结:
要防止用' or ''='登陆后台,应该说是要防止SQL 注射
防止SQL注射,个人认为最简单的注意方法有两个,1,检查所有输入,凡字符型,过滤掉单引号,并且用单引号引起来.
比如
user=replace(request(user),"'")
select * from admin where user='"&user&"'
如果是数字型,直接用
id=int (request(id))
select * from blog where id="&id&"
个人认为这样基本上就差不多了,有错误的地方欢迎大家指正 呵呵 对于‘等特殊字符的提交 我以为应该是得自VB的数据类型的模糊(有时数据可以为字符串,也可以是整型。。。。) ASP可以说是vb的一个子集(某种程度上) 所以就继承了那个数据类型模糊的漏洞 楼上对数据类型的严格限制 我想的确是个很不错的方式 不过我想也可以将返回的信息页面转换成静态的html页面 不是那种传统的asp的页面表达方式 比如这个页面[url]http://www.tbhacker.com/www/Soft/hkdh/200506/20050625054422.html[/url] 呵呵 乱说的 不对的地方请指教 [code]
objRs.open "Select * From [t4dmin] where cP4ssword='" & MD5(strPass)& "'",conn,1,1
If objRs.eof and objRs.bof Then
fError()
Else
bolIs = False
For i = 1 to objRs.RecordCount
If objRs("c4dmin")=strName then
bolIs = True
Exit For
End If
Next
If bolIs = False Then
fError()
Else
session("IsAdmin")=True
response.Redirect("admin.asp")
End If
End If
objRs.close
[/code] 我觉得职业欠钱说得有点搞笑了吧?
user字段俺这么输:
admin' or password='123456
...
你为什么不直接用户名输入admin
密码框输入123456呢?
再说了
sql="select * from ad where user='"&user&"'"
rs.open sql,conn,1,1
if password=rs("password") then
有没看到还有一个判断密码字段和用户输入的是否一致的检查,可不是简单的
if not(rs.eof and rs.bof)的判断呀,晕! 防范的方法很多嘛
1.破坏构造的字符串
(1) MD5加密:不管怎么样,字符串都将转换成MD5值,不存在与SQL语句冲突的问题.当然,如果是MSSQL的话,用户名要放在句后,以防一些人用;--注释掉MD5的Hash(一般有MD5存在的,ACCESS饶不过应该,MSSQL可以看先后次序)
(2) 过滤特殊字符,比如replace(username,"'","") | replace(username,"--","")之类的..(网上N多系统这么过滤)
2.将数据与字符串比对
先查看是否eof,如果不是,那么将数据库中取出一个未知的值与输入的值比对.如果相同,则可以说明正确...也是杜绝的办法(看过一个叫ET-Comic的代码使用这种方法)
3.先取出再验证
一个比较低效率的办法...来一个循环,然后读出数据比对...输入的字符串本身不参与查询...(很早的时候看一个文章系统用过..) 在程序中增加过滤输入单引号的过滤函数,如果输入单引号的密码时出现“不能输入带有’的字符”这样的对话框。
if (document.login.Uname.value.indexOf9"'")>-1
{
validity=false;alert("不能输入带有’的字符");
document.login.Uname.focus();
return validity;
} 楼上的方法只是在做表面...其实是可以修改提交参数的 呵呵,对,好像是删掉那段过滤的函数,再改一下提交到的网站,可以饶过,在本地登陆 [quote][b]下面是引用haicao于06-30-2005 13:19发表的:[/b]
我觉得职业欠钱说得有点搞笑了吧?
user字段俺这么输:
admin' or password='123456
...
.......[/quote]
表中字内容如下:
[user] [password]
admin !@$$%Q@$
test 123456
asdfas klajsdigaklj;
SQL语句执行的结果是什么?(当我们在user字段输 admin' or password='123456)
select * from [admin] where [user]='admin' or password='123456'
if password=rs("password") then
现在这里可以认证成功了吧?
如果你直接输
admin
123456
SQL语句是
select * from [admin] where [user]='admin'
这时候得到的东西才不可能使if password=rs("password") then逻辑成立呢 你上面的写法我试过。
执行结果得到的数据应该是:
admin !@$$%Q@$
test 123456
主要是因为admin记录在test对应记录之前
所以 程序逻辑能不能通过还是得看点运气了。。。。。 如果你还想过滤and,分号,--什么的,请看下面(完全过滤了在SQL注入中最关键的5个字符串。)
Replace(Replace(Replace(Replace(Replace(变量名称,"'",""),"or",""),"and",""),"--",""),";","")
我要是提交o;ra;nd-;- 过滤后看看or and -- 还不是存在 [quote][b]下面是引用影に尸鬼封尽于2005-08-10 10:46发表的:[/b]
如果你还想过滤and,分号,--什么的,请看下面(完全过滤了在SQL注入中最关键的5个字符串。)
Replace(Replace(Replace(Replace(Replace(变量名称,"'",""),"or",""),"and",""),"--",""),";","")
我要是提交o;ra;nd-;- 过滤后看看or and -- 还不是存在[/quote]
这主意不错。哈哈。。。。。。。。。。。。。。
看来过滤某些字符未必有意义,还是发现危险字符随时结束掉比较安全。。。 最核心的办法应该是检查它是不是数字
if isnumeric(id)=false then
response.redirect "index.asp"
end if
Replace只会越弄越繁 不用检测
提交的时候直接定义成数字 提交的时候再检查
不如当初就直接定义为数字(数字参数)
偶有个问题
疑惑很久了的
字符型的参数,只过滤单引号,能否绕过过滤而进行注射?
topic=request("topic")
topic=replace(topic,"'","''") '单引号变两个单引号
SQL语句为
SQL="select * from [article] where [topicname]='"&topic&"'"
请问有没有可能绕过 也发表一个我的看法。
把传值在过滤的同时,用 left() 限制住,到底限制多少大家心里应该有个底,不管如何SQL语句,都是缺胳膊少腿,这样就没耐何了。
此法不足之处就是查询中,这个字数限制多少倒是需要好好考虑。
楼上的问题我也带着这疑问呵,不知道用UNION构造会不会绕过,因为这时都没用到 单引号。自己试过没成功。希望高人现身解答。 [quote][b]下面是引用marquee于2005-08-14 19:53发表的:[/b]
也发表一个我的看法。
把传值在过滤的同时,用 left() 限制住,到底限制多少大家心里应该有个底,不管如何SQL语句,都是缺胳膊少腿,这样就没耐何了。
此法不足之处就是查询中,这个字数限制多少倒是需要好好考虑。
.......[/quote]
UNION 就能跳过单引号吗?
select * from [user] where [username]='xxxx'
xxxx怎么可能不用单引号而跳过去?
union在里面也被处理成字符而已... 2楼的如下:
<%
user=request.form("userID")
pass=request.form("password")
for i=l to len(userID)
cl=mid(userID,i,l)
if cl="'"or us="="or" us="<"or us=">" then
response.redirect"提交非法字符,IP已经记录"
response.end
end if
next
%>
3楼的如下:
<%
user=request.form("userID")
if instr(user,"'")<>0 then
response.redirect "提交非法字符,IP已经记录"
response.end
end if
%>
这俩个那个更有效果一些呢 最简单的办法..
在验证帐号密码那
全部给他md5
无论什么字符
全部都md5以后,哈哈,都不过过滤了
md5(request.form(username))
md5(request.form(password))
当然了
数据库的帐号密码也要全部都md5过 [quote][b]下面是引用andyower于2005-08-22 15:52发表的:[/b]
最简单的办法..
在验证帐号密码那
全部给他md5
无论什么字符
全部都md5以后,哈哈,都不过过滤了
.......[/quote]
md5是不错,但是如果每提交一次都全部md5是不是执行效率要大大降低呢?我觉得应该是,尤其是对于比较大型的站点 。 呵呵
根本不会
第一:在注册的时候才会进行一次md5
既然已经把密码都md5过,何况多加一个用户名md5呢
第二:在登陆的时候,把输入的帐号和密码md5,和上面的说法一样的
根本不会影响到什么 解决的一个小例子
if (document.form1.textfield.value.indexOf("<")!=-1 || document.form1.textfield.value.indexOf(">")!=-1)
{
alert("用户名中包含非法字符 (<,>) ");
document.form1.textfield.focus();
return false;
}
if (document.form1.textfield.value.indexOf("'")!=-1)
{
alert("用户名中包含非法字符 (') ");
document.form1.textfield.focus();
return false;
} 楼上的似乎在客户段执行的javascrip代码
很容易饶过去的 在客户端执行的话,保存下来然后去掉相关限制代码然后提交可以突破。
对此可以限制不允许站外提交,否则提交:禁止从站点外部提交数据!
当然对此也有突破的办法,抓包在数据包中加:Referer: [url]http://localhost/[/url]
从这里看来,要做到安全就从底层杜绝。
另外,如果实在要过滤的话,只过滤某些东西永远不如只允许某些东西来的安全!! [quote][b]下面是引用xhr于2005-06-04 23:37发表的:[/b]
<%
user=request.form("userID")
pass=request.form("password")
for i=l to len(userID)
cl=mid(userID,i,l)
.......[/quote]
你这句我测试过了,居然还是可以登录。
<%
name=Request.Form("username")
pass=Request.Form("password")
for i=1 to len(username)
fuck=mid(username,i,1)
if fuck="'" or fuck=" " or fuck="%" or fuck="<" or fuck=">" then
response.write "非法参数,你小子想干嘛!"
response.end
end if
next
%>
用账号:aa 密码:a' or '1'='1依然可以登录。。。。。知道为什么吗?
因为你看这段话,只检测了账号呀,也就是没有检测密码,呵呵。。。不过这种循环的思路不错!
对于这个过滤问题,我用以下两个办法解决的:
1。replace( )方法
name=Replace(trim(Request.Form("Username")), "'", "''")
Pass=Replace(trim(Request.Form("Password")), "'", "''")
2。instr( )方法
<%
name=trim(request.Form("username"))
pass=trim(request.Form("password"))
if name="" or pass="" then
response.Write "<script language=javascript>alert('对不起用户名/密码
未输入,请返回!');history.back(-1)</script>"
elseif instr(name,"'")<>0 or instr(pass,"'")<>0 then
Response.Write "<script language=javascript>alert('用户名/密码错误,
请重新输入!');history.back(-1)</script>"
response.end
end if
%>
当然还可以加密呀,先查询之类的,不喜欢麻烦呵呵,欢迎指点哦:) 关于这句登陆验证
rs.open "select * from admin where usr='" & usr & "' and psw='" & psw & "'",conn,1
假如查询的假如查询的结果不为空算是通过验证
那么除了'or'1'='1可以成功之外
把'or'8'or'作为usr和psw的值,能否通过验证?是什么原因呢?
我测试下来结果是可以通过,并且不给psw赋值也可以,不太懂(其实那个8也可以不要)
指点偶下:) 不是过滤单引号而且把长度限制一下就OK了吗?
还用那么麻烦? 哇` 讨论的太激烈了` 这篇文章可以加精了`
` 先收下 等会慢慢研究` 谢咯~ 手册11月有一篇
只是写的用这个直接进入个站点
写的和没水平 [quote][b]下面是引用liuyes于2005-09-02 16:40发表的:[/b]
不是过滤单引号而且把长度限制一下就OK了吗?
还用那么麻烦?[/quote]
你真天真。。 许多程序员就跟你一样的想法以至于现在还有那么多程序存在注射。。 [quote][b]下面是引用andyower于2005-08-22 15:52发表的:[/b]
最简单的办法..
在验证帐号密码那
全部给他md5
无论什么字符
全部都md5以后,哈哈,都不过过滤了
.......[/quote]
我汗...你连USERNAME都给MD5掉...那论坛啥的谁还能见着谁的名字那? 说下我的看法,可以把用户名先放到数据库里查询下验证下存在不存在,如果存在然后再验证数据库的用户名和密码对不对,不过这种办法效率好象降低了 [quote][b]下面是引用影に尸鬼封尽于2005-08-10 10:46发表的:[/b]
如果你还想过滤and,分号,--什么的,请看下面(完全过滤了在SQL注入中最关键的5个字符串。)
Replace(Replace(Replace(Replace(Replace(变量名称,"'",""),"or",""),"and",""),"--",""),";","")
我要是提交o;ra;nd-;- 过滤后看看or and -- 还不是存在[/quote]
确实 这样的法子还是有问题的
[s:51] 我写的一个过滤函数:
Function xufang0219(badstr)
badstr_in="'|<|>|%|=|chr(32)|?|&|;|,|chr(34)|chr(9)| |$|"""
blockit=Flase
Dim Array_Filter
Array_Filter=Split(badstr_in,"|")
For i=0 To UBound(Array_Filter)
If Instr(lcase(badstr),Array_Filter(i))<>0 Then
badstr=replace(badstr,Array_Filter(i),"")
blockit=True
Exit For
End If
Next
If blockit=true Then
response.write "<script language=javascript>"
response.write "alert('旭方提示:\n您提交的数据中含有非法字符!');"
response.write "history.back();"
response.write "</script>"
End If
xufang0219=badstr
End Function
在需要调用的地方使用:
username=xufang0219((trim(request.form("username")))
username=xufang0219((trim(request.form("username")))
即可,大家也可根据实际情况添加和删除需要过滤的字符,这样就可以过滤用户输入部分的特殊字符了 ,从而防止非法用户在输入部分输入特殊字符引起跨站脚本攻击! 感觉我这样过滤是比较完美的,如下:
<%
if request("action")="logincheck" then
dim chkname,chkpass,rs
function checkstr(xsstring)
xsstring=replace(xsstring,"<","<")
xsstring=replace(xsstring,">",">")
xsstring=replace(xsstring,"'","\")
checkstr=xsstring
end function
username=Lcase(checkstr(trim(request.Form("username"))))
password=Lcase(checkstr(trim(request.Form("password"))))
if username="" or password="" then
response.Write"<script>alert('用户名或密码不允许为空!');"
response.Write "location.href='admin_login.asp'</script>"
response.End
end if
for i=1 to len(username)
chkname=mid(username,i,1)
if chkname="%" or chkname="#" or chkname="'" or chkname="(" then
response.write "<script>alert('用户名中出现非法字符!');"
response.write "location.href='admin_login.asp'</script>"
response.End
end if
next
for i=1 to len(password)
chkpass=mid(password,i,1)
if chkpass="%" or chkpass="'" or chkpass="(" or chkpass="#" then
response.Write "<script>alert('密码中出现非法字符!');"
response.Write "location.href='admin_login.asp'</script>"
response.End()
end if
next
%>
页:
[1]