• 6275阅读
  • 0回复

Cgi安全点滴[精华][supfrie] [复制链接]

上一主题 下一主题
离线XChinux
 

只看楼主 倒序阅读 楼主  发表于: 2005-07-30
写了不少介绍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”及其大小写变形,但是没有考虑到操作系统对于文件名的命名规范。拿[晕倒死] 来说,至少“\”,“/”,“:”,“*”,“?”,“””,“<”,“>”和“|”是不能作为文件名的,因此类似“system”“的用户名实际上等效 于“system”。

regex v0.4
$user =~s /\\\/:\?”<>\|//sig;
$user =~ /^system$/
精确匹配“system”及其大小写变形,并且考虑了操作系统对文件名的命名规范。但是没有注意[晕倒死] 文件名不能以 空格开头。

regev v0.5
$user =~s /\\\/:\?”<>\|//sig;
$user =~s /^ //sig;
$user =~ /^system$/;
精确匹配“system”及其大小写变形,考虑了操作系统对文件名的命名规范,同时也注意到[晕倒死] 文件名不能以空格 开头。

注意: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查询语句 的执行。

拿前几天发现的一个国内商业站点来举例子(考虑到该站点的名誉,下面的数据是虚构的)。

该站点属于允许用户注册的那种,同时提供了一个查询好友的功能,可以按照号码和昵称查询。由于前些日子看跨站 脚本执行漏洞多了些,因此不容分说在号码域输入了“<h1>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 <DIR> Apache
2002-01-24 13:22 <DIR> Becky!
2001-06-04 11:38 <DIR> Documents and Settings
2001-11-27 12:57 <DIR> fport
2001-12-06 09:56 <DIR> Inetpub
2001-07-05 14:22 <DIR> jdk1.3.1
2001-09-03 09:59 10,000 kill.exe
2002-01-28 12:31 <DIR> mysql
2001-08-07 13:38 <DIR> nc11nt
2001-12-31 12:44 <DIR> Perl
2001-06-06 15:43 <DIR> php
2002-03-12 14:52 <DIR> PPDownload
2002-03-10 14:22 <DIR> Program Files
2001-09-03 10:00 49,152 ps.exe
2002-03-12 20:26 110 test.pl
2002-02-28 11:08 <DIR> tomcat
2001-09-06 17:20 45,056 who.exe
2002-01-24 10:48 <DIR> wincmd
2002-03-10 17:29 <DIR> [晕nt]
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 <DIR> Apache
2002-01-24 13:22 <DIR> Becky!
2001-06-04 11:38 <DIR> Documents and Settings
2001-11-27 12:57 <DIR> fport
2001-12-06 09:56 <DIR> Inetpub
2001-07-05 14:22 <DIR> jdk1.3.1
2001-09-03 09:59 10,000 kill.exe
2002-01-28 12:31 <DIR> mysql
2001-08-07 13:38 <DIR> nc11nt
2001-12-31 12:44 <DIR> Perl
2001-06-06 15:43 <DIR> php
2002-03-12 14:52 <DIR> PPDownload
2002-03-10 14:22 <DIR> Program Files
2001-09-03 10:00 49,152 ps.exe
2002-03-12 20:26 110 test.pl
2002-02-28 11:08 <DIR> tomcat
2001-09-06 17:20 45,056 who.exe
2002-01-24 10:48 <DIR> wincmd
2002-03-10 17:29 <DIR> [晕nt]
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服务器泄露源代码的漏洞,很多都是与没有考虑到操作系统而造成的。

安全是一个整体,如果没有这个概念,我想要提高系统的安全性恐怕很难,甚至是不可能的。

基本就这么多要说的吧,如果其中有什么错误或者是遗漏,欢迎给我发信指正。
二笔 openSUSE Vim N9 BB10 XChinux@163.com 网易博客 腾讯微博
承接C++/Qt、Qt UI界面、PHP及预算报销系统开发业务
快速回复
限100 字节
 
上一个 下一个