写了不少介绍CGI安全的文章,基本上已经没有多少可写的东西了。这里只是将一些不好归类或者是以前没有讲的东西做一个归纳总结,希望对提高大家的CGI安全水平有些帮助。 【正则表达式安全】 正则表达式大家都还记得吧?不会吧?没办法!:(我下面就以Perl语言为例再简单介绍一下它的主要内容,下次一定要记得。 锚模式: ^:仅匹配串首 $:仅匹配串尾 \b:匹配单词边界 \B:匹配单词内部 匹配选项: g:匹配所有可能的模式 o:只赋值一次 m:将串视为多行 s:将串视为单行 i:忽略大小写 x:忽略模式中的空白 替换选项: g:改变模式中的所有匹配 o:仅赋值一次 m:将待匹配串视为多行 s:将待匹配串视为单行 i:忽略模式中的大小写 x:忽略模式中的空白 e:替换字符串作为表达式 翻译选项: c:翻译所有未指定字符 d:删除所有指定字符 s:把多个相同的输出字符缩成一个 字符范围转义: \d:任意数字,[0-9] \D:除数字外的任意字符,[^0-9] \w:任意单词字符,[_0-9a-zA-Z] \W:任意非单词字符,[^_0-9a-zA-Z] \s:空白,[ \r\t\n\f] \S:非空白,[^ \r\t\n\f] 其它特殊字符: ():模式内存 .:匹配除换行外的所有字符 :一个或多个相同的字符 *:0个、1个或多个相同字符 ?:0个或1个该字符 [ ]:一组字符中的一个 [^ ]:除其之外的所有字符 { }:出现次数 |:选项 注:以上内容是从flamephoenix翻译的Perl 5中文教程整理所得。 基础知识有了,下面我们谈谈关于正则表达式安全的一些问题。为了清楚起见,主要分为匹配,替换和翻译三种情况。 1. 匹配 编写CGI程序时经常要遇到一个问题,如何匹配某个字符串,我们以LB5000为例,考虑如何过滤用户名“system”: regex v0.1: $user =~ /system/; 可以匹配“system”,但是无法匹配类似“System”的用户名。 regex v0.2 $user =~ /system/i; 可以匹配“system”及其大小写变形,无法注册类似“dossystem”的用户名。 regex v0.3 $user =~ /^system$/I; 精确匹配“system”及其大小写变形,但是没有考虑到操作系统对于文件名的命名规范。拿Windows来说,至少“\”,“/”,“:”,“*”,“?”,“””,“<”,“>”和“|”是不能作为文件名的,因此类似“system”“的用户名实际上等效于“system”。 regex v0.4 $user =~s /\\\/:\?”<>\|//sig; $user =~ /^system$/ 精确匹配“system”及其大小写变形,并且考虑了操作系统对文件名的命名规范。但是没有注意Windows文件名不能以空格开头。 regev v0.5 $user =~s /\\\/:\?”<>\|//sig; $user =~s /^ //sig; $user =~ /^system$/; 精确匹配“system”及其大小写变形,考虑了操作系统对文件名的命名规范,同时也注意到Windows文件名不能以空格开头。 注意:regex v0.5中的两个替换表达式顺序是不能随意改变的,一般顺序是先替换后匹配。 当然,这只是演示而已,并没有达到最好的效果。另外还应该注意用户名长度以及DOS设备文件等因素,但是这些都不在本文的讨论范围内。 由上可见,匹配表达式应该考虑到多种情况,不应该光是表面上的一些东西。例如,匹配字符串时还需要考虑应该精确匹配还是模糊匹配,如果是精确匹配的话,除了使用“^”和“$”外,也许还得使用“\b”和“\B”。而模糊匹配则一般不使用这些锚模式,而是使用“i”和“x”等匹配选项。 另外,匹配表达式还需要考虑匹配选项“m”和“s”的问题,无论是精确匹配还是模糊匹配都要考虑。在某些情况下,巧妙的利用程序在这两个选项上的错误使用可以绕过某些限制。匹配模式“g”和“o”也是容易混淆的地方,缺省情况下为“o”,这些问题也是可以利用的。字符范围转义的具体定义和特殊字符也是需要注意的,如果你不是经常编程,最好在使用之前查一下手册。 2. 替换 需要注意,替换部分可以使用模式次序变量$n,但是不能使用模式中的特殊字符,例如: File:test3.pl $aaa = “123abc”; print $aaa.”\n”; $aaa =~ s/(\d )/[$1]/; print $aaa.”\n”; C:\>perl test3.pl 123abc [123]abc C:\> 可以看到替换结果正如所料,我们再来看下面的例子: File:test4.pl #!/usr/bin/perl $aaa = "123abc"; print $aaa."\n"; $aaa =~ s/(\d )/*/; print $aaa."\n"; C:\>perl test3.pl 123abc *abc C:\> 本来是特殊字符的“*”并没有发生作用,其实想想也知道,这些都不是固定的内容,放在替换部分怎么行呢? 替换操作主要还是注意那些替换选项的使用,可以参考匹配部分相关描述。 3. 翻译 替换操作可能是一对一,一对多,多对一或者多对多,而翻译操作从某个角度来看,则只能是一对一。 看看下面的例子即可: File:test6.pl #!/usr/bin/perl $aaa = "123456789"; $aaa =~ tr/12/abc/; print $aaa."\n"; $aaa = "123456789"; $aaa =~ tr/123/abc/; print $aaa."\n"; $aaa = "123456789"; $aaa =~ tr/1234/abc/; print $aaa."\n"; C:\>perl test6.pl ab3456789 abc456789 abcc56789 C:\> 另外,也需要注意相关选项的使用方法。还有一点需要注意的是,翻译操作对象是字符,可不是汉字。 【SQL语句安全】 其实我对SQL语句安全知道得不多,所以这里只说一种最简单,同时也是最常见的问题,即用户可干预SQL查询语句的执行。 拿前几天发现的一个国内商业站点来举例子(考虑到该站点的名誉,下面的数据是虚构的)。 该站点属于允许用户注册的那种,同时提供了一个查询好友的功能,可以按照号码和昵称查询。由于前些日子看跨站脚本执行漏洞多了些,因此不容分说在号码域输入了“ haha”,结果返回下面的信息: Error: Unable to perform query: select * from users where xx_no = haha :You have an error in your SQL syntax near ' haha' at line 1 看出来没有,我们可以干扰SQL查询语句的执行!:) 重新在号码域输入“1 or password=123456”,哈哈。。。得到了7000多个密码为“123456”的用户,国内用户的安全意识看来还不够高嘛! 这里再说一点,为什么我知道密码域为“password”呢?不是瞎猜的,也不是随便尝试常见名称,注册时需要填写初始密码的,一个站点的密码域的名字一般情况下应该一样吧?! 得到一个结论,跨站脚本执行漏洞可以为SQL语句漏洞提供启发作用!:P 【错误处理安全】 这里所谓的“错误处理安全”指的是利用程序运行出错绕过某些限制,JFS入侵PCWEEK的文章大家都读过吧? 我们举个例子来解释一下: File:test.pl #!/usr/bin/perl $fname = "A" x 10; $fname .= "\n"."XXX"; open("F",">$fname"); close(F); print "haha\n"; 运行脚本之前: C:\>dir 驱动器 C 中的卷是 WIN2K 卷的序列号是 7429-4F77 C:\ 的目录 2002-02-28 10:23 Apache 2002-01-24 13:22 Becky! 2001-06-04 11:38 Documents and Settings 2001-11-27 12:57 fport 2001-12-06 09:56 Inetpub 2001-07-05 14:22 jdk1.3.1 2001-09-03 09:59 10,000 kill.exe 2002-01-28 12:31 mysql 2001-08-07 13:38 nc11nt 2001-12-31 12:44 Perl 2001-06-06 15:43 php 2002-03-12 14:52 PPDownload 2002-03-10 14:22 Program Files 2001-09-03 10:00 49,152 ps.exe 2002-03-12 20:26 110 test.pl 2002-02-28 11:08 tomcat 2001-09-06 17:20 45,056 who.exe 2002-01-24 10:48 wincmd 2002-03-10 17:29 WINNT 4 个文件 104,318 字节 15 个目录 186,761,216 可用字节 C:\> 运行脚本: C:\>perl test.pl haha C:\> 运行脚本之后: C:\>dir 驱动器 C 中的卷是 WIN2K 卷的序列号是 7429-4F77 C:\ 的目录 2002-02-28 10:23 Apache 2002-01-24 13:22 Becky! 2001-06-04 11:38 Documents and Settings 2001-11-27 12:57 fport 2001-12-06 09:56 Inetpub 2001-07-05 14:22 jdk1.3.1 2001-09-03 09:59 10,000 kill.exe 2002-01-28 12:31 mysql 2001-08-07 13:38 nc11nt 2001-12-31 12:44 Perl 2001-06-06 15:43 php 2002-03-12 14:52 PPDownload 2002-03-10 14:22 Program Files 2001-09-03 10:00 49,152 ps.exe 2002-03-12 20:26 110 test.pl 2002-02-28 11:08 tomcat 2001-09-06 17:20 45,056 who.exe 2002-01-24 10:48 wincmd 2002-03-10 17:29 WINNT 4 个文件 104,318 字节 15 个目录 186,761,216 可用字节 C:\> 我们可以看到创建文件的语句没有成功运行,因为脚本运行前后C:\>根目录下没有产生预期的文件,而下面的print语句则成功运行了。 也就是说,我们可以在一定程度上控制程序的执行结果。在CGI程序中,如果“$fname”是一个用户赋值的变量,而创建文件部分是某些处理“$fname”变量的脚本,那么我们就可能通过精心构造这个“$fname”变量而绕过这些处理脚本,Right? 解决的办法比较简单,改为如下的脚本即可: File:test2.pl #!/usr/bin/perl $fname = "A" x 10; $fname .= "\n"."XXX"; open("F",">$fname") || die (“Error found!\n”); close(F); print "haha"; 一定要注意die()函数中的“\n”,否则就要泄露物理路径了!:) 这和意外情况处理失败类似,可以对照着看看。 【整体安全】 安全是一个整体,这句话不是很容易理解吧?它的范围实在太广了,大到整个网络系统,小到某个变量。 拿前些日子的SecurityFocus跨站脚本执行漏洞为例,他们虽然仔细检查了用户提交的FirstName和LastName变量的内容,但是却没有考虑到这两个变量在网页中的位置是相邻的。因此导致了跨站脚本执行漏洞的产生。 其实这样的例子实在太多,例如前面讨论的匹配表达式的问题,我们不仅要看其本身是否安全,还要注意到与之配合的操作系统。还有各种WEB服务器泄露源代码的漏洞,很多都是与没有考虑到操作系统而造成的。 安全是一个整体,如果没有这个概念,我想要提高系统的安全性恐怕很难,甚至是不可能的。 基本就这么多要说的吧,如果其中有什么错误或者是遗漏,欢迎给我发信指正。 |
|小黑屋|最新主题|手机版|微赢网络技术论坛 ( 苏ICP备08020429号 )
GMT+8, 2024-10-1 12:14 , Processed in 0.178916 second(s), 12 queries , Gzip On, MemCache On.
Powered by Discuz! X3.5
© 2001-2023 Discuz! Team.