一个常用的减缓和迷惑攻击者的技术就是改变web服务器的标识。Web服务器通常会在每个HTTP响应的Server报头中包含服务器的标识。Apache在这个方面更加明显,缺省情况下不只发送完整的名字和版本号,而且还允许服务器模块把它们的版本也附加上。
为了改变Apache web服务器的标识,你可能不得不查看源码,会发现“Apache”这个名字是硬编码的,然后改变它,重新编译服务器。你可以使用SecServerSignature指示达到同样的效果:
SecServerSignature "Microsoft-IIS/5.0"
需要注意的是,虽然这个方法工作的很好,但是熟练的攻击者(和工具)可能使用其他的技术来对web服务器进行“指纹识别”。例如,缺省文件、错误信息、发出报头的顺序、服务器响应特定请求的方式和其他类似的方法——都能泄漏真正的标识。我会在mod_security的将来的发行版中查看进一步的对标识隐藏的改进。
如果你改变了Apache的签名,但是被错误日志中奇怪的信息打扰(一些模块仍然可见——这个只影响错误日志,从外部看它仍然如期望的工作):
[Fri Jun
11
mod_ssl/2.8.12
OpenSSL/0.9.6b configured -- resuming normal
operations
你应该重新组织模块加载的顺序使mod_security最后运行,正如为chrooting所做的解释。
In order for this directive to work you must
leave/set ServerTokens to
Full.
ModSecurity包含对Apache文件系统隔离,或叫chrooting的支持。Chrooting是将应用程序限制在文件系统指定部分的过程,有时叫做“监狱(jail)”。一旦采用了chroot(change root的简写)操作,应用程序就不能再访问监狱外的内容。只有root用户可以越狱。Chrooting过程的一个重要部分就是在监狱中不允许任何和root有关的东西(root进程或root suid命令)。这个思想是如果是一个攻击者试图通过web服务器闯入的话,他没有太多的事情可作,因为他也在监狱里,没有办法逃出去。
应用程序不是必须支持chrooting。任何应用程序可以用chroot命令来被chroot。下列行:
chroot /chroot/apache /usr/local/web/bin/apachectl start
将用/chroot/apache内的内容替换文件系统后再启动Apache。
不幸的是,事情不是这么简单。问题是应用程序通常需要共享库,和其他各种文件和命令来以便正常工作。所以,为了让它们工作,你必须拷贝需要的文件,使它们在监狱中可用。这不是一个简单的任务(关于如何chroot Apache web服务器的详细介绍,请看这里http://penguin.epfl.ch/chroot.html)。
有一天当我在chroot一个Apache的时候,我意识到这个过程很枯燥,于是我开始寻找简化的方法。结果是,我在mod_security模块里加入了chrooting功能,使整个过程不那么复杂了。你使用手头的mod_security,你只需在配置文件中添加一行:
SecChrootDir /chroot/apache
你的web服务器就会被成功地chroot了。
除了简单以外,mod_security的chrooting还带来了另一个优势。与外部的chrooting(前边提到的)不同,mod_security的chrooting不需要在监狱中存在额外的文件。Chroot调用发生在web服务器的初始化之后,派生(子进程)之前。因此,所有的共享库都已经被加载了,所有的web服务器模块都已经初始化了,所有的日志文件都已经打开了。你只需要把自己在监狱里的数据。
但是,有一些情况,你需要jail中有一些额外的文件,就是在你试图执行CGI脚本或是系统命令的时候。他们可能有自己的文件方面的需求。如果你属于这类情况,你需要象通常一样继续使用外部的chroot过程,但是你仍然不需要考虑Apache本身。
在Apache 2.x中,AcceptMutex指示的缺省值是pthread。有时在使用chroot功能的时候,这个设置会导致Apache不能工作。把AcceptMutex设置成其他的值可以解决这个问题(比如posixsem)。
如果你配置chroot使日志文件留在监狱外边,Apache会拥有指向指向监狱外部的文件的文件描述符。Chroot机制最初不是为了安全设计的,很多人对这感到不安。决定权在你自己。把这个特性当作一个“验方”来用吧。
Apache用于认证(authentication)的文件必须放在监狱内部,因为它们会在每次收到请求的时候被打开。
正如前边提到的,chroot调用必须在Apache初始化的一个特定时刻调用,正好在其他的所有模块初始化完成后。这意味着ModSecurity必须在模块列表的首位。为了保证这一点,你可能需要使用下边的配置指示对模块排序做些改动:
ClearModuleList
AddModule mod_security.c
AddModule ...
AddModule ...
AddModule ...
第一个指示清空了列表。紧接着必须是ModSecurity,后边是其它你打算使用的模块(除了http_core.c,它是自动添加的,你不必担心它)。你可以执行httpd命令的时候加上 -l 参数来得到内置模块的列表:
./httpd -l
如果你选择把Apache命令和支持文件放在监狱的外边,你将不能使用命令apachectl graceful和apachectl restart。那需要Apache访问监狱外的内容,而这是不可能的。如果是Apache 2.x,即使命令apachectl stop也不能工作。在以后的版本中,我会开发一些替代脚本来解决这个问题
使用Apache 2.x,你不能手工配置模块排序,因为Apache 2.x已经内置对了模块排序的支持。ModSecurity使用这个特性通知Apache 2.x什么时候调用它,chroot就生效了(如果你遇到了问题请通知我)。
Apache启动进程的方法有所变化。现在httpd命令自己会创建一个包含了进程号的pid文件。因此你需要保证Apache在监狱内外的文件夹相同。假设你的Apache在监狱外放在“/usr/local/web/apache”,而你的监狱放在/chroot目录里,那么你必须创建一个目录“/chroot/usr/local/web/apache/logs”。
启动之后,Apache会在那里创建一个pid文件(假设你没有在你知道自己在作什么的情况下,在httpd.conf中修改pid文件的位置)。
如果你在你的平台上遇到了问题,了解mod_security实现chroot隔离的。模块排序是必须的是因为我们没有改动服务器的源代码。另外一个需要克服的问题是每个模块都会被初始化两次。这样会有问题是因为在初始化函数中我们不能分辨是被第一次还是第二次调用。(实际上,在Apache 2中是可以的,但是在Apache 1中不可以)ModSecurity使用一个锁文件,在第一次初始化阶段创建它,然后在第二次初始化的时候删除。
缺省,这个文件在日志文件夹中创建。相对于web服务器的根的目录“logs/modsec_chroot.lock
”。使用SecChrootLock指示可以改变存放的路径。
从1.8版开始,无论任何原因,如果ModSecurity的chroot操作失败,它会阻止服务器启动。如果它没有在配置阶段,而是在运行时刻监测到了chroot操作失败,会在错误日志中记录失败有关的信息,然后退出子进程。这样可能不够好,但是总好过你误以为chroot监狱的保护仍然存在,而Apache并没有运行在其中。
作为ModSecurity能力的一个例子,我们会给你演示如何用它监测和预防常见的安全问题。我们在这里不会详述问题本身的细节。但是在Open Web Application
Security项目的指南中可以找到:http://www.owasp.org。
如果你的脚本需要和文件系统打交道,你需要留意某些元字符和结构。例如,路径中的字符组合“../”是向上一级目录的请求。
在正常的操作中不需要请求中出现这个字符组合,所以你可以用下面的过滤器禁止它们:
SecFilter "\.\./"
在攻击者在你的Web页面中注入HTML或是Javascript代码,而这些代码被其他用户执行的时候,就发生了跨站脚本攻击(XSS)。这种攻击通常是在把HTML加在你通常不希望看到它们的地方。成功的XSS攻击可以使攻击者获得你的会话的cookie,进而获得对应用程序的全部访问权限。
对这种攻击的合理防御是使用参数过滤(从而去除讨厌的HTML/Javascript),但是你通常需要在不改变已有应用程序的前提下保护它们。可以用下边的过滤器达到目的:
SecFilter "<script"
SecFilter "<.+>"
第一个过滤器只针对使用<script>标签的Javascript注入进行保护。第二个更通用,不允许参数中存在任何HTML
使用这个过滤器的时候要谨慎,因为很多应用程序需要参数中包含HTML(例如,CMS应用程序,论坛等等)。这种情况下你可以使用选择性过滤器。比如,你可以使用第二个过滤器作为全站的规则,但是之后可以用下面的代码放宽对特定脚本的规则:
<Location
/cms/article-update.php>
SecFilterInheritance Off
# other
filters here ...
SecFilterSelective "ARGS|!ARG_body"
"<.+>"
</Location>
这段代码片段只接受命名参数body中的HTML。现实中,你可能在列表中添加几个参数。
今天的多数Web应用程序在数据处理上都严重依赖数据库。除非在安全执行数据库操作时方面非常小心,攻击者很可能把任意的SQL命令直接注入数据库。这可能导致攻击者获得敏感数据,修改它们甚至把他们全部从数据库中删除。
这样的过滤器:
SecFilter "delete[[:space:]]+from"
SecFilter "insert[[:space:]]+into"
SecFilter "select.+from"
可以保护你免受多数的SQL相关的攻击。这个只是例子,你需要根据你实际使用的数据库引擎,仔细制作自己的过滤器。
Web应用程序有时执行操作系统命令来完成一些操作。持之以恒的攻击者可能找到这方面的漏洞,使他能够在系统中执行任何命令。这样的过滤器:
SecFilterSelective ARGS "bin/"
在Unix类的操作系统中会检测执行存放在各个文件夹的命令的企图。
缓冲区溢出是溢出程序的运行栈,添加汇编指令,并试图执行它们的技术。在一些情况下,用类似这样的行有可能阻止这类攻击:
SecFilterByteRange 32 126
因为它只接受有这个范围内的字节构成的请求。是否使用这类取决于你的应用程序和使用的字符编码。
如果你希望支持多个范围,正则表达式正好雪中送炭。你可以使用类似下边的:
SecFilterSelective THE_REQUEST "!^[\x0a\x0d\x20-\x7f]+$"