3 内部伤害
到目前为止,仅仅考虑了通过Web例览站点的人——从几千里之外——可能带来的潜在的安全危险。但实际上还存在另一种离得更近的危险因素。
在CGI安全问题上常犯的一种错误是忘记了本地用户。尽管通过Web浏览站点的人不影响本地安全,如文件保护和所有者,但Web服务器的本地用户却能这样,必须做出更多努力防止这些入侵。大部分多用户系统上,如UNIX,Web服务器是作为一个程序运行的,而机器仍被许多人使用做着许多事情。仅仅因为为某人与自己一起工作或访问自己的学校并不意味着他能抵制住诱惑,而不去捣鼓Web安装从而引起问题。
3.1 CGI脚本用户
大部分Web服务器是作为运行CGI脚本的特殊用户而安装的。这是在CGI程序运行时拥有该CGI程序的用户,并且他所拥有的权限能限制该脚本能做什么事情。
在UNIX下,服务器自己也是作为root(系统的超级用户或管理员)运行的,并允许它使用端口80作为浏览器与之通信的地方(只有root能使用这些被称为"保留的"端口0到1023;所有用户都可以使用其余的端口)。当服务器执行CGI程序时,大部分Web服务器都能设置为以另外一个用户而不是Web服务器本身来运行该程序——尽管不是所有服务器都能这么做。
将CGI脚本作为root运行是很危险的!服务器应被设为利用一个普通用户,如常用的nobody来运行CGI脚本。用户权限越小,运行的CGI脚本能造成的危害就越小。
3.2 Setuid 危险
编程者还应知道自己的UNIX CGI脚本中是否设置Setuid位。如果对某个可执行文件允许该选项,将能使该程序与拥有该文件的用户有同样权限,而不是执行它的用户。如果自己的CGI脚本上设置setuid位,无论服务器作为什么用户来运行它,它的权限都等同于该文件的拥有者。这当然有很大的隐患--可能会对以其权限运行脚本的用户失去控制。幸运的是Setuid位很容易被禁止。对所有CGI脚本执行chmod a-s即能关闭所有的setuid,程序即能以允许的权限运行。
当然,在某些情况下也许希望设置setuid位--例如如果脚本需要以特殊用户身份来运行以访问一个数据库。在这种情况下,必须加倍小心确保该程序的其他文件保护能将可以访问它的用户限制在允许范围内。
3.3 "Community" Web服务器
即使Web服务器以一个常用的用户来执行脚本,仍有一个潜在的问题,那就是一个人并不总是能控制服务器。如果许多人共同控制服务器,每个人都可以将CGI脚本安装作为nobody用户来运行。这就使这些人的任何一个都可以利用CGI程序访问他们原先不能访问的地方,而这些地方是nobody允许进入的。
也许潜在的安全问题的解决办法是将CGI的控制限制为一个人。在某些情况下尽管这似乎是合理的,但对较大站点却经常不太可能。例如,一个大学有几百个学生,每个学生都想试着去编写并安装CGI脚本。
3.4 使用CGI Wrap
当有多个用户可以访问CGI时,对于确定脚本以什么用户运行的问题的一个较好的解决办法是CGI wrap程序。CGI Wrap,可以在using CGI Web站点中找到,是一个简单的包装,它以拥有该文件的用户而不是服务器指定的用户来运行CGI脚本。这种简单的预防措施使脚本拥有者对它可能的危害负责。
因为CGI wrap使得CGI脚本的作者负责他们自己的脚本权限,所以它不仅是一个保护其他人拥有的重要文件的有力的工具,而且是促使人们编写安全的脚本的有力的工具。只有他们自己的文件会处于危险之中,这样的现实对脚本作者会是极大的促进。
3.5 CGI脚本权限
还应该清楚了解CGI脚本被哪个用户拥有以及脚本自身的文件权限。包含脚本的目录的权限也非常重要。
例如,如果Web服务器上的cgi-bin目录是所有人可写的,那任何本地用户将能删除CGI脚本并用另一个来代替。如果脚本本身是所有人可写的话,那么任何人将能修改脚本完成任何事情。
请看下面这段无害的UNIX CGI脚本:
#!/bin/sh
#Send the header
echo"Content-type:tex/html"
echo""
#Send some HTML
echo "<HTML><HEADER><TITLE>Fortune</1TLE><HEADER>
echo "<Body>Your fortune:<HR><PRE>
forune
echo"</BODY><HIML>"
现在,如果脚本上设置的权限允许某个恶意的用户将程序改变如下:
#!/bin/sh
#Send the header
echo "content-type:text/html"
echo""
#Do some damage!
rm-rf/
echo"<HTML><TITLE>Got you! <TITLE><BODY>"
echO"<H1>Ha ha!<H1></BODY></HTML>"
那么下一个在Web上访问该脚本的用户即使他没做什么坏事也会导致大量问题。在Web上检查用户输入的完整性很重要,但更重要的是保证脚本本身未被修改且不能被修改。
3.6 本地文件安全
脚本在本地硬盘上创建的文件的完整性也同样重要。在得到Web用户输入的一个合理的文件名之后,使用该文件名干什么也很重要。根据Web服务器运行的操作系统,权限和拥有者信息可以与文件中的数据一起存在文件上。
例如,UNIX系统能记录文件访问权限,包括创建该文件的用户的权限、同组用户的权限、以及系统其他人的权限。windows NT使用的是一个更复杂访问控制清单系统,但完成的功能大致相同。根据这些标志如设置以及授予或禁止什么权限,Web服务器机器的用户也可能引起麻烦。
例如,在创建一个文件时就应知道给它设置的权限。大部分Web服务器软件将umask或权限码设为0000,意味着可以创建一个任何人可读写的文件。尽管文件上的权限设置对在Web上浏览的人可能没什么不同,但本地访问的用户却能利用不严格的权限设置造成危害。基于这种现实,应该尽可能严格地限制文件权限。
保证每个打开文件的调用都有一个最小限制集合的最简单的办法是设置脚本的umask。umask()是一个UNIX调用,它能对每个后续的文件创建限制权限。umask()的参数是一个数字,用于对后续的文件创建的权限码进行屏蔽。如果umask为0022,则不管在打开文件时给组用户和其他用户赋予了什么显式的权限,都将导致创建的文件仅能被用户自己写。即使已经设置了umask,创建文件时也应该显式指定权限。如果只有CGI脚本能访问文件,那么只有运行CGI程序的用户才能访问该文件——权限为0600。如果另一个程序需要访问该文件,可以使该程序的拥有者成为与CGI脚本同一组的用户,这样只需设置组用户权限——权限为0660。如果必须让所有人都能访问该文件,应使该文件只能读,不能写——权限为0644。
3.7 使用显式路径
最后,本地用户还可以最后一种方式攻击Web服务器——欺骗服务器运行他写的一个外部程序,而不是运行在CGI脚本中指定的程序。下面是一个简单的程序,从UNIX的fortune命令可以看出该浏览者还比较聪明。
#!/bin/sh
# Send the header
echo"conten_type:text/html"
echo""
#Send the fortune
echo"<HTML><HEADER><TITLE>Fortune</TITLE></HEADER><BODY>"
echo "<You crack open the cookie and the fortune reads:<HR><PRE>"
fortune
echo "</PRE><BODY></HTML>"
该脚本看起来可一点没有害处。它不接收用户输入,所以用户不能籍此搞什么把戏。因为它仅由Web服务器运行,所以脚本本身的权限设置可以非常严格,可以防止任何有企图的本地用户修改它。如果对该脚本所在的目录也设置了正确的权限的话,看起来就没什么地方可以出问题了,是不是?
当然还有问题。记住得要有点偏激。
上述程序清单调用了外部程序,在本例中是echo和fortune。因为这些脚本没有用显式路径指明它们在硬盘上的位置,该shell即使用PATH环境变量来找到它们,从变量中的每一项查找要执行的程序。这可能很危险。例如,如果fortune程序安装在/usr/games中,但PATH中在它之前列出了/TMP,那么任何碰巧命名为"fortune"并位于临时目录的程序都会被执行,而不是真正的fortune。
该程序可以做它的创建者想做的任何事情,可以删除文件,也可以登记有关请求信息并将数据传给真正的fortune——使用户和编程者谁也不聪明。在CGI脚本中运行外部程序时一定要指定显式的路径。PATH环境变量有很大作用,但它与其他变量一样也能被非法使用.
4 使用他人CGI脚本时的注意事项
关于CGI,可以从很多地方获得信息——从Internet上,从学校图书馆中,从像本书这样的书中,UseNet组中以及朋友和同事中。从这些地方不仅可以获得信息,还可以得到实际的程序和库。有些程序和库如果已经有人做过了为什么自己还要从头再做一遍呢?但就像不能盲目听从别人的意见一样,关于如何理财,如何驾车或者生活中的别的方面,同样,也不能在自己的服务器上盲目地运行另从的代码。从Net上得到的脚本也可能真正是很好的脚本。但也许并不是。花些时间考察一下脚本的来源以及获取它的站点的可靠性是值得的。
4.1 追根求源
某些Web拥有者。如果不能看到并研究源代码的话,他们甚至都不会运行一个公共的、免费的或商业性的脚本。这可能有点偏激。如果某个声誉很好的公司销售一个文档详细且广为使用的脚本,该脚本应该比自己写的脚本更安全一些。原因有二。首先,专业人才知道并能避免一些常见的安全漏洞;其次,公司是为了嫌钱而做生意,如果他们以次充好或销售那些恶意的产品就不能再做生意赚钱了。
从另一方面来看,如果UseNet组中看到一个编译好的可执行文件出自一个从没听说过的人,没有什么文档可以看,也没有该程序的用户可以交流交流,那么在将它放入自己的服务器之前一定要仔细考虑。也有可能这是来自一个像自己一样的另一个CGI编程者的完全合法的贡献,目的是想让全世界共享他的编程成果。但它也可能来自某个恶意的,具有变态幽默感的,只想看到自己能使多少人清盘的人。
在评价公共的免费软件或商业性软件时,应考虑下面这些方面:
该脚本来自一个声誉好的站点吗?该站点存在很长一段时间了吗?它维护得好吗?Web拥有者在发布文件前进行检查吗?
有没有足够的文档说明该程序如何工作以及用户如何使用等信息?
有多少人已经下载了该脚本?该站点愿意提供顾客名单吗?(仅在有疑问时才去询问;Web拥有者不会整天去回答这类问题。)
有人在UseNet上讨论该脚本吗?如果有,他们说好还是不好?如果没人提到该脚本可以进一步请求别人的见解。一般总会有人响应的。
提示
在评价脚本时检查下面这些useNet组: comp.security.announce,comp.securiy.unix,以及comp.infosystem.www.authority.cgi。另外还可以访问位于ftp.cert.org的Computer
Emergency Response Team,以了解安全问题的历史及有关工作以及安全保护的软件。
5)该脚本的作者有没有一些别的好名声的脚本?
6)源代码能得到吗?免费的或有价的都行。
7)该程序是不是过份宣传它的能力?如果是,这可能是一个编程新手。
8)该站点自己运行了该脚本吗?如果没有,为什么?能找到别的站点运行该脚本吗?过分偏激以及时间限制
尽管游览取自Web的所有代码是个好主意,但要花费很多时间,特别是当代码比较复杂时更是如此。
例如,NCSA HTTPd就太大了,一般用户不可能一行行去读,但是从它的主站点http://www.ncsa.uiuc.edu下载它却能保证极好的完整性,满足任何用户的需要。实际上,任何从NCSA下载的东西都是有保障的。
实际上,Web上的许多著名的站点已经为用户做了大部分的几乎偏激的代码检查工作。从它闪中下载代码是可能利用的另一层另一层保护。这些站点包括:
ftp://ftp.ncsa.uiuc.edu/Web/httpd/Unix/ncsa_httpd/cgi(the NCSA Archive)
http://www.novia.net/~geewhiz(Virtual Webwerx Division Zero-CGI Land)
http://www.lpage.com/cgi(the World-Famous Guestbook Server)
http://sweetbay.will.uiuc.edu/cgi++(cgi++)
http://www.aee.com/wdw(the Web Developers Warehouse)
4.2 注意礼貌
最后,如果确实希望从Web上下载一些CGI代码,或者完整地使用它,或者用作自己编写的更大程序的一部分,还应了解一些事情。
代码是兔费的并不意味着可以自由地用它作自己想做的任何事情。通常程序和库是禁止拷贝的,如果原始作者没有放弃这个权力,他即能限制如何使用该程序。例如,作者可能禁止拆散该脚本,及禁止用作别的脚本的一部分。
一跟来说,在使用别人的代码之前(即使已经确定它是安全的),最好与作者进行联系取得许可。至少这样做比较有礼貌。而大部分情况下,作者会很高兴他的代码能被别人利用。当然,如果在自己程序某个片段处注明原始作者将是很礼貌的。