SQL Injection 漏洞利用攻击, JS 脚本, HTML 脚本攻击在网上似乎逾演逾烈。陆续的很多站点都被此类攻击所困扰,并非像主机漏洞那样可以当即修复,来自于 WEB 的攻击方式使我们在防范或者是修复上都带来了很大的不便。 HOOO...... 一个站长最大的痛苦莫过于此。自己的密码如何如何强壮却始终被攻击者得到,但如何才能做到真正意义上的安全呢 ? 第一,别把密码和你的生活联系起来 ; 第二, Supermaster 的 PWD 最好只有你自己知道 ; 第三,绝对要完善好你的网站程序。然而怎样才能完善,这将是我们此文的最终目的。
安全防护,如何做到安全防护 ? 想要防护就要知道对方是如何进行攻击。有很多文章都在写如何攻下某站点,其实其攻击的途径也不过是以下几种:
1. 简单的脚本攻击
此类攻击应该属于无聊捣乱吧。比如 ****:alert(); 等等,由于程序上过滤的不严密,使攻击者既得不到什么可用的,但又使的他可以进行捣乱的目的。以目前很多站点的免费服务,或者是自身站点的程序上也是有过滤不严密的问题。
2. 危险的脚本攻击
这类脚本攻击已经过度到可以窃取管理员或者是其他用户信息的程度上了。比如大家都知道的 cookies 窃取,利用脚本对客户端进行本地的写操作等等。
3. Sql Injection 漏洞攻击
可以说,这个攻击方式是从动网论坛和 BBSXP 开始的。利用 SQL 特殊字符过滤的不严密,而对数据库进行跨表查询的攻击。比如:
http://127.0.0.1/forum/showuser.asp?id=999 and 1=1 http://127.0.0.1/forum/showuser.asp?id=999 and 1=2 http://127.0.0.1/forum/showuser.asp?id=999 and 0<>(select count(*) from admin) http://127.0.0.1/forum/showuser.asp?id=999'; declare @a sysname set @a='xp_'+ 'cmdshell' exec @a 'dir c:/'---&aid=9 |
得到了管理员的密码也就意味着已经控制的整站,虽然不一定能得到主机的权限,但也为这一步做了很大的铺垫。类似的 SQL Injection 攻击的方式方法很多,对不同的文件过滤不严密所采取的查询方式也不同。所以说想做好一个完整的字符过滤程序不下一凡功夫是不可能的。
4. 远程注入攻击
某站点的所谓的过滤只是在提交表格页上进行简单的 JS 过滤。对于一般的用户来说,你大可不必防范 ; 对早有预谋的攻击者来说,这样的过滤似乎根本没作用。我们常说的 POST 攻击就是其中一例。通过远程提交非法的信息以达到攻击目的。
通过上面的攻击方法的介绍,我们大致的了解了攻击者的攻击途径,下面我们就开始重点的介绍,如何有效的防范脚本攻击 !
让我们还是从最简单的开始:
防范脚本攻击
JS 脚本 和 HTML 脚本攻击的防范其实很简单: server.HTMLEncode(Str) 完事。当然你还不要大叫,怎么可能 ? 你让我把全站类似都加过滤我还不累死 ? 为了方便的过滤,我们只需要将 HTML 脚本和 JS 脚本中的几个关键字符过滤掉就可以了:程序体 (1) 如下:
Username=CHK(replace(request("username") , "'" , "")) |
使用 Include 把函数写在公有页面上,这样效率是最好的。
程序体 (1)
另外,值得我们注意的是,很多站点在用户注册,或者是用户资料修改的页面上也缺少脚本的过滤,或者是只在其中之一进行过滤,注册进入后修改资料仍然可以进行脚本攻击。对用户提交的数据进行检测和过滤,程序体 (2) 如下:
以下是过滤函数
If Instr(request("username") , "=")>0 or Instr(request("username") , "%")>0 or Instr(request("username") , chr(32))>0 or Instr(request("username") , "?")>0 or Instr(request("username") , "&")>0 or Instr(request("username") , ";")>0 or Instr(request("username") , " , ")>0 or Instr(request("username") , "'")>0 or Instr(request("username") , "?")>0 or Instr(request("username") , chr(34))>0 or Instr(request("username") , chr(9))>0 or Instr(request("username") , " ")>0 or Instr(request("username") , "$")>0 or Instr(request("username") , ">")>0 or Instr(request("username") , "<")>0 or Instr(request("username") , """")>0 then response.write " 朋友,你的提交用户名含有非法字符,请更改,谢谢合作 返回 " response.end end if |
程序体 (2)
为了提供工作效率我们再将过滤内容程序化,这样对多个参数的过滤效率将有很大程度上的提高:如 程序体 (3)
以下为程序主体
dim Bword(18) Bword(0)="?" Bword(1)=";" Bword(2)=">" Bword(3)="<" Bword(4)="-" Bword(5)="'" Bword(6)="""" Bword(7)="&" Bword(8)="%" Bword(9)="$" Bword(10)="'" Bword(11)=":" Bword(12)="|" Bword(13)="(" Bword(14)=")" Bword(15)="--" Bword(16)=" chr(9)" Bword(17)=" chr(34)" Bword(18)=" chr(32)" errc=false |
以下是应用实例部分
for i= 0 to ubound(Bword) if instr(FQYs , Bword(i))<>0 then errc=true end if next if errc then response.write " " response.end end if |
程序体 (3)
有了上面的过滤函数您可以在任何需要过滤的地方应用过滤函数直接使用就可以了。这就使我们的修复工作大大的简化了。
另外,我想在这里再次多提醒一下,一些站点的 UBB 在进行小的表情图标转化时也会出现过滤问题,由于很隐蔽所以不容易发现:
如:
我们标签内的文字进行修改,不知道各位看懂没,前一个单引号用来中和程序提供的左引号,第二个单引号用来中和闭合的右引号,这样程序输出就为:
如果图片不存在,那么将激活 onerror 标签执行脚本程序。对于已经过滤了单引号的站点在这里用双引号一样可以完成。对于过滤了 **** 字段的,只用 alert() 也完全可以。所以说要过滤就要过滤完全,别给攻击者留下一丝机会。
防范 SQL Injection 漏洞攻击
可以这样说,这里似乎是整篇文章的重点了 .SQL Injection 漏洞攻击的的多样化也使得我们在程序防护上不得不想的更多一些。面对 SQL Injection 的强大 " 攻势 " ,我们到底该过滤哪些 ?
一些常用的危险字符有 :
' 数据库字段判别封闭
-- 某些数据库注释标志
# 某些数据库注释标志
" 可能导致程序出错
/ 跨越目录
3221143836nicode 编码的特征字符
$ 可能用于变量标注
/ 和 / 一样
NULL 小心 " 空 " 录入的危险,可能导致数据库或系统处理报错,利用报错构造溢出 .
空格和 ' 一起,构造 sql injeciton
? = & 如果存在二次参数传递,可能改写 querystr 。
(1) 从最一般的 .SQL Injection 漏洞攻击来看:用户名和密码上的过滤问题,如:
提交:用户名为: 'or''=' 用户密码为: 'or''='
从程序出发,我们完全可以得出,数据库在执行以下操作 :
Sql=" SELECT * FROM lUsers WHERE Username=''or''='' and Password = ''or''=''" |
这样一来,这样, SQL 服务器将返回 lUsers 表格中的所有记录,而 ASP 脚本将会因此而误认为攻击者的输入符合 lUsers 表格中的第一条记录,从而允许攻击者以该用户的名义登入网站。对此类注入的防范似乎简单的很:
利用以下程序就可以实现,程序体 (4)
strUsername = Replace(Request.Form("Username") , "''" , "''''") strPassword = Replace(Request.Form("Password") , "''" , "''''") |
程序体 (4)
(2) 杜绝 SQL 注入式攻击的第一步就是采用各种安全手段监控来自 ASP request 对象 (Reques 、 Request.QueryString 、 Request.Form 、 Request.Cookies 和 Request.ServerVariables) 的用户输入,以确保 SQL 指令的可靠性。具体的安全手段根据你的 DBMS 而异。
SQL 注入式攻击可能引起的危害取决于该网站的软件环境和配置。当 Web 服务器以操作员 (dbo) 的身份访问数据库时,利用 SQL 注入式攻击就可能删除所有表格、创建新表格,等等。当服务器以超级用户 (sa) 的身份访问数据库时,利用 SQL 注入式攻击就可能控制整个 SQL 服务器 ; 在某些配置下攻击者甚至可以自行创建用户帐号以完全操纵数据库所在的 Windows 服务器。如:
http://127.0.0.1/forum/showuser.asp?id=999';declare @a sysname set @a='xp_'+ 'cmdshell' exec @a 'dir c:/'--&aid=9 http://127.0.0.1/forum/showuser.asp?id=999'; declare @a sysname set @a='xp'+ '_cm'+'dshell' exec @a 'dir c:/'--&aid=9 |
甚至可以执行像: net user fqy fqy /add 这样的指令 . 当然这就需要你当前的运行身份必须是 Sa ,或者你攻击的只是一台虚拟主机,我劝你还是就此打住 .
对于一些整机使用的站点来说防止通过 80 端口攻击而直接拿到整机管理权限,这一点就变得至关重要了。对 xp_cmdshell 的过滤就成为首要,很多站点的程序都是用 GET 或者是 GET 与 POST 混合来提交数据的,对于此,我们给出一种防止 GET 进行 SQL 注入的程序:如程序体 (5)
fqys=request.servervariables("query_string") dim nothis(18) nothis(0)="net user" nothis(1)="xp_cmdshell" nothis(2)="/add" nothis(3)="exec%20master.dbo.xp_cmdshell" nothis(4)="net localgroup administrators" nothis(5)="select" nothis(6)="count" nothis(7)="asc" nothis(8)="char" nothis(9)="mid" nothis(10)="'" nothis(11)=":" nothis(12)="""" nothis(13)="insert" nothis(14)="delete" nothis(15)="drop" nothis(16)="truncate" nothis(17)="from" nothis(18)="%" errc=false for i= 0 to ubound(nothis) if instr(FQYs , nothis(i))<>0 then errc=true end if next if errc then response.write " " response.end end if |
程序体 (5)
我要做点声明的是:以上的程序只是对 GET 方式提交的数据进行的过滤,千万不要盲目套用。
像其他一些来自 ASP request 对象 (Reques 、 Request.QueryString 、 Request.Form 、 Request.Cookies 和 Request.ServerVariables) 的用户输入的攻击方法的方法,大致都集中在脚本期望的输入变量是数字变量 (ID) 上,当然我们不能只看数字变量,比如:
http://127.0.0.1/systembbs/showtopic.asp?tid=99&name=abc' and left(userpassword , 1)='a http://127.0.0.1/systembbs/addtopic.asp?tid=99&name=abc' and userpassword='or''=' |
另外,如何单一的防止类似这样的注入错误 ?
http://127.0.0.1/systembbs/addtopic.asp?tid=99' ;delete forum_forum;--&page=33 |
防范程序 : 程序体 (6)
......addtopic.asp?action=add...... ......addtopic.asp?action=delect...... Action1=trim(Request.QueryString()) if left(action1 , 7)<>"action=" then ' 限定 querystring 必须为 action= error(err01)' 错误处理 else action=Request.querystring("action")' 取得 querystring 的值 end if select case action' 对 querystring 进行处理 case "add" ..... case "delete" ...... case else ' 如果 querystring 没有这个值则进行错误处理 error(err02) end select |
程序体 (6)
出现这样的攻击,使我们的站长们不得不又再次头痛,这里我可以给出大家一个解决最好办法,一般的来说,用户名长度字符数不会超过 15 个字符,大都为 14 字符。那么我们从长度出发,来进行过滤:如程序体 (7)
Name=replace(name , "'" , "") If len(name)>16 then Response.write " 你要做什么 ?" Response.end End if |
程序体 (7)
继续回到我们的主题, " 脚本期望的输入变量是数字变量 (ID)". 怎样进行注入防范,天呐,方法太多了,最直接的就是判断是否是数字整型,还有一些比较个性的验证办法,我们一一介绍一下 如 : 程序体 (8)
一,判断数字是否是整型
p_lngID = CLng(Request("ID")) |
二 取字长 这一点我相信一般的数据长度不会大于 8 位所以 :
If len(ID)>8 then response.write "bedpost" response end end if |
三 我认为这是一种比较冒险的办法,就是再进行一次数据库的查询,如果数据库表内没有相同的值与之相同那么返回错误 .
sql = "SELECT NAME FROM Category where ID="&ID set temp=conn.Execute(SQL) if temp.bof or temp.eof then response.Redirect("index.asp") else cat_name=temp("name") end if set temp=nothing |
上面的是数据 ID 的检测,下面则是正式的查询
sql = "SELECT ID T_ID , NAME FROM Category where ID="&ID&" ORDER BY xh asc" rs.open sql , conn , 1 , 1 |
四,我自己常用的数据过滤脚本
id=replace(id , "'" , "") If len( request("id"))>8 then ‘ 为什么取长度上面程序中已经说明 response.write " " response.end else If request("id")<>"" then ‘ 取不为空则是为了防止一些程序页中会出现空值情况,如果不在这里做判断,程序会校验出错 . If IsNumeric(request("id"))=False then ' 风清扬修改 ID 数据监控程式 response.write " " response.end end if end if end if |
程序体 (8)
由于我个人的编程习惯,我喜欢将所有的数据检验程序全部保留到整站的公用程序中,比如 :conn.asp 啦,只需要写一次就可以修复全站的问题 .
说到这里,我提一点关于攻击的问题,就是跑用户密码或者是用户名,一般常用的就是
....../show.asp?id=1 and 0<>(select count(*) from admin where id=3 and left(username , 1)='a') |
这样去一个一个尝试,当然我们不能在这里提什么 Perl 程序去跑密码,程序是别人写,要自己知道原理 . 这里我只是想给个比较方便的办法就是取 ASC 码范围 . 这个要比单独跑要快很多 . 不论是是字母,数字,汉字,特殊字符,他们总会有对应的 ASC 码,用以下办法 :
....../show.asp?id=1 and 0<>(select count(*) from admin where id=3 and asc(right(left(username |
e , 3) , 1)) between 1 and 10000) 剩下的就随你了,一般的从 97 到 122 就可以啦,字母嘛,很快 D. 呵呵,有人想用 mid 函数当然也是不错 asc(mid(username , 2 , 1)) between 1 and 10000 也成 .
如何更加有效的防止 SQL 注入攻击 ? 我们将在下面的文章中具体提到 !
防范远程注入攻击
这类攻击在以前应该是比较常见的攻击方式,比如 POST 攻击,攻击者可以随便的改变要提交的数据值已达到攻击目的 . 又如 :COOKIES 的伪造,这一点更值得引起程序编写者或站长的注意,不要使用 COOKIES 来做为用户验证的方式,否则你和把钥匙留给贼是同一个道理 .
比如 :
If trim(Request. cookies ("uname"))="fqy" and Request.cookies("upwd") ="fqy#e3i5.com" then ........more......... End if |
我想各位站长或者是喜好写程序的朋友千万别出这类错误,真的是不可饶恕 . 伪造 COOKIES 都多少年了,你还用这样的就不能怪别人跑你的密码 . 涉及到用户密码或者是用户登陆时,你最好使用 session 它才是最安全的 . 如果要使用 COOKIES 就在你的 COOKIES 上多加一个信息, SessionID ,它的随机值是 64 位的,要猜解它,不可能 . 例 :
if not (rs.BOF or rs.eof) then login="true" Session("username"&sessionID) = Username Session("password"& sessionID) = Password ‘Response.cookies("username")= Username ‘Response.cookies("Password")= Password |
下面我们来谈谈如何防范远程注入攻击,一般的攻击都是将单表提交文件拖到本地,将 Form ACTION="chk.asp" 指向你服务器中处理数据的文件即可 . 如果你全部的数据过滤都在单表页上,那么恭喜你,你将已经被脚本攻击了 .
怎么才能制止这样的远程攻击 ? 好办,请看代码如下 :
程序体 (9)
‘ 个人感觉上面的代码过滤不是很好,有一些外部提交竟然还能堂堂正正的进来,于是再写一个 .
‘ 这个是过滤效果很好,建议使用 .
if instr(request.servervariables("http_referer") , "http://"&request.servervariables("host") )<1 then
response.write " 处理 URL 时服务器上出错。
如果您是在用任何手段攻击服务器,那你应该庆幸,你的所有操作已经被服务器记录,我们会第一时间通知公安局与国家安全部门来调查你的 IP. "
response.end
end if
程序体 (9)
本以为这样就万事大吉了,在表格页上加一些限制,比如 maxlength 啦,等等 .. 但天公就是那么不作美,你越怕什么他越来什么 . 你别忘了,攻击者可以突破 sql 注入攻击时输入框长度的限制 . 写一个 SOCKET 程序改变 HTTP_REFERER? 我不会。网上发表了这样一篇文章:
------------len.reg----------------- Windows Registry Editor Version 5.00 [HKEY_CURRENT_USER/Software/Microsoft/Internet Explorer/MenuExt/ 扩展 (&E)] @="C:/Documents and Settings/Administrator/ 桌面 /len.htm" "contexts"=dword:00000004 -----------end---------------------- -----------len.htm------------------ ----------end----------------------- |
用法 : 先把 len.reg 导入注册表 ( 注意文件路径 )
然后把 len.htm 拷到注册表中指定的地方 .
打开网页,光标放在要改变长度的输入框上点右键,看多了一个叫扩展的选项了吧
单击搞定 ! 后记 : 同样的也就可以对付那些限制输入内容的脚本了 .
怎么办 ? 我们的限制被饶过了,所有的努力都白费了 ? 不,举起你 de 键盘,说不。让我们继续回到脚本字符的过滤吧,他们所进行的注入无非就是进行脚本攻击。我们把所有的精力全都用到 ACTION 以后的页面吧,在 chk.asp 页中,我们将非法的字符全部过滤掉,结果如何 ? 我们只在前面虚晃一枪,叫他们去改注册表吧,当他们改完才会发现,他们所做的都是那么的徒劳。
ASP 木马
已经讲到这里了,再提醒各位论坛站长一句,小心你们的文件上传:为什么论坛程序被攻破后主机也随之被攻击者占据。原因就在 ...... 对 !ASP 木马 ! 一个绝对可恶的东西。病毒么 ? 非也 . 把个文件随便放到你论坛的程序中,您老找去吧。不吐血才怪哦。如何才能防止 ASP 木马被上传到服务器呢 ? 方法很简单,如果你的论坛支持文件上传,请设定好你要上传的文件格式,我不赞成使用可更改的文件格式,直接从程序上锁定,只有图象文件格式,和压缩文件就完全可以,多给自己留点方便也就多给攻击者留点方便。怎么判断格式,我这里收集了一个,也改出了一个,大家可以看一下: 程序体 (10)
判断文件类型是否合格
Private Function CheckFileExt (fileEXT) dim Forumupload Forumupload="gif , jpg , bmp , jpeg" Forumupload=split(Forumupload , " , ") for i=0 to ubound(Forumupload) if lcase(fileEXT)=lcase(trim(Forumupload(i))) then CheckFileExt=true exit Function else CheckFileExt=false end if next End Function |
验证文件内容的合法性
set MyFile = server.CreateObject ("Scripting.FileSystemObject") set MyText = MyFile.OpenTextFile (sFile , 1) ' 读取文本文件 sTextAll = lcase(MyText.ReadAll): MyText.close |
判断用户文件中的危险操作
sStr ="8|.getfolder|.createfolder|.deletefolder|.createdirectory| .deletedirectory" sStr = sStr & "|.saveas|wscript.shell|script.encode" sNoString = split(sStr , "|") for i = 1 to sNoString(0) if instr(sTextAll , sNoString(i)) <> 0 then sFile = Upl.Path & sFileSave: fs.DeleteFile sFile Response.write " "& sFileSave &" 文件中含有与操作目录等有关的命令 "&_ " "& mid(sNoString(i) , 2) &" ,为了安全原因,不能上传。 "&_ " " Response.end end if next |
程序体 (10)
把他们加到你的上传程序里做一次验证,那么你的上传程序安全性将会大大提高 .
什么 ? 你还不放心 ? 拿出杀手锏,请你的虚拟主机服务商来帮忙吧。登陆到服务器,将 PROG ID 中的 "shell.application" 项和 "shell.application.1" 项改名或删除。再将 "WSCRIPT.SHELL" 项和 "WSCRIPT.SHELL.1" 这两项都要改名或删除。呵呵,我可大胆的说,国内可能近半以上的虚拟主机都没改过。
小结
如何更好的达到防范 SQL Injection 的攻击 ? 这里我个人给推荐几个办法,第一,免费程序不要真的就免费用,既然你可以共享原码,那么攻击者一样可以分析代码。如果有能力的站长最好还是更改一下数据库表名,字段名,只修改关键的 admin , username , password 就可以了,比如 forum_upasswd 这样的字段名谁能猜到 ? 如果你猜到了,最好赶快去买彩票吧,特等奖不是你还会有谁呢 ? 另外,一般站点的关键就在于管理员的密码,很好的保护好你的管理员密码那是至关重要的,至少 10 位的数字字母组合。另外加上现在大多数站点程序都会使用 MD5 来加密用户密码,加上你密码的强壮性,那样你站点的安全性就大大的提高了。即使出现了 SQL Injection 漏洞,攻击者也不可能马上拿下你的站点。