Copyright © 2002-2005 Ivan Ristic
中文版: 我爱臭豆腐,sakulagi,Alan.Jin http://www.chinaunix.net
中文版备注:
随着使用apache得人越来越多,和使用web服务得广泛.web服务器得安全问题也是一个至关重要得问题.但是在国内真正能够做web服务安全得感觉并不是很多(实际上也有很多是抄袭免费软件的概念和技术).而且即使是有得话相对来讲成本也是很高的.所以我希望能够找到一个价格相对低廉而且又有效的方法来防护web服务的安全.
当初打算翻译这个的另外一个原因是,经常浏览国外的安全方面的网站和一些外国站点.发现在apache上面安装ModSecurity 或者是介绍web安全的人100%或者是说99.99%都在介绍和使用这个插件.但是在国内的资料上面并没有看到这些东西或者是一带而过了.我本人外语不好当然了中文的水平也不好.但是我想这么好的一个软件应该叫更多的人熟悉和了解.我希望以后在有朝一日能够叫每个apache的服务器都安装这个插件用来抵御来自界的攻击.但是这个只是一个理想或者是梦想.
这里要非常感谢sakulagi,Alan.Jin 他们两个人的外语都比我好.特别是sakulagi 英文水平和中文水平都很好.如果大家要感谢的话应该是多感谢他们.我只是想他们两个人推荐这里这个软件和引入了要翻译这个手册的想法.
最后想说的是安装了这个软件并不是一个大力丸能够防止任何攻击和渗透.任何软件和工具都不是无敌的.都是需要管理员精心的配置和发挥他的最大的功效.
Modsecurity 是一个开放原代码的入侵检测和防护引擎,用来保护Web应用程序.他同样和可以当作一个Web应用程序防火墙.它嵌入到Web服务器中,担当一个强大的保护伞-保护来自应用程序的攻击.
ModSecurity 和web服务器结合,增强web服务器抗攻击的能力.一些只得关注的功能说明:
l 过滤请求:在web服务器或者其他模块获得handled之前, 按照原来的样子分析进入的请求.(严格的说,在这种嵌入式的操作里面,有一些操作在没有到达ModSecurity之前不可避免的进行了一些操作.)
l Anti-evasion 技术: paths and parameters are normalised before analysis takes place in order to fight evasion techniques.
l 了解HTTP协议:引擎获得了http的协议后,将进行非常详细的颗粒过滤.例如,它可以查看任何一个单独的参数,或者是制定的cookie值.
l POST 有效负载分析:这个引擎能够截取传送的内容使用POST方法.
l 审计记录:能够详细的记录每一个请求(包括POST)可以被用在法律分析上.
l HTTPS 过滤:当这个引擎被嵌入到web服务器中后,可以有权访问解密后的数据请求.
l 过滤被压缩的内容:和上面一样,安全引擎可以有权访问到被解压缩后的内容.
ModSecurity 能够被用于发现攻击,或者是发现和阻止攻击.
ModSecurity is available under two licenses. Users can choose to use the software under the terms of the GNU General Public License (http://www.gnu.org/licenses/gpl.html), as an Open Source / Free Software product. Alternatively, a variety of commercial licenses is available: end-user licenses for individual or site-wide deployment, OEM licenses for closed-source distribution with applications, web servers, or security appliances. For more information on commercial licensing please contact Thinking Stone Ltd:
Thinking Stone Ltd
Tel: +44 845 0580628
Fax: +44 870 7623934
http://www.thinkingstone.com
如果是没有这么多好人建立了Apache web服务器,和一些好人花费大量的时间来营造Apache的模块,我将不能经常学习Apache的模块设计.我特别感谢设计和编写mod_rewrite.的人们.
ModSecurity 的开发是由Ivan Ristic 和 Thinking Stone.欢迎注释和重要反馈.请发邮件到[email protected]
备注:
请不要发给我关于技术问题的邮件这个邮件地址是我私人信箱.我不能使用我的私人时间来处理这些技术问题.用户自己可以从mail archives寻找答案.如果你需要快速的回答问题和你想保证反映时间请考虑从Thinking Stone公司购买商业服务.
在你安装之前你需要选择适合你自己的安装方法.第一你需要选择是否安装最新的版本从ModSecurity 的CVS目录当中(里面有最新的特性,但是不稳定).或者是安装最新的稳定版本(推荐).如果你选择稳定版的话,你可以安装ModSecurity 的二进制文件.它已经被从原代码编译完成.
下面的一些内容可以帮助你更好的选择一个适合自己的方法来安装它.
如果你想要最新版本的模块话可以从CVS库中得到.可以从一个web站点获得从最近的一个稳定版本的被改变的列表(在CHANGES这个文件中).ModSecurity 的CVS库存放在SourceForge(http://www.sf.net).你可以直接通过这个地址访问和查看http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/mod-security/.
下载源程序到你的计算机.需要执行下面两个命令:
cvs -d:pserver:[email protected]:/cvsroot/mod-
security login
cvs -z3 -d:pserver:[email protected]:/cvsroot/mod-
security co mod_security
第一行是你使用anonymouse用户登陆,第二行是下载全部的可用的文件从CVS库中.
如果是不喜欢CVS,但是你还想要最新的版本你可以下载最新的nightly tar包从下面的地址:
http://www.modsecurity.org/download/snapshot/mod_security-snapshot.tar.gz 新功能被一一的加入到了mod_security中,用来测试在每一次改变之后.这方法可以确保从CVS上面有永远可用的版本下载.
可以从http://www.modsecurity.org/download/ 下载到稳定的版本.如果你不下载源代码分发的版本时.不时的可以有一些二进制的分发.如果有他们在下载页面的列表上.
从源程序安装有两个方法:一个是将模块和web 服务器编译到一起去.或者是编译mod_security.c 成动态共享对象(DSO).
DSO安装方法非常容易,安装方法有两种流派在Apache的安装方法中.第一要解压缩文件到一个地方(必须要作的),编译模块的命令:
/apachehome/bin/apxs -cia mod_security.c
在这之后你需要关闭和从新启动Apache 服务(如果你尝试从新启动它你可能会segfault)
/apachehome/bin/apachectl stop
/apachehome/bin/apachectl start
我知道有一些人使用的平台上没有apxs这个工具安装到系统上.这个工具在一些Unix的平台上的版本是独立的包.这个问题出现在安装的时候不是缺省安装.解决这个问题的方法是从你的软件厂商那里查找如何建立自定义的Apache模块.(在一些RedHat 平台上你需要安装http-devel 包后就有了apxs这个应用程序).
在静态的编译模块时,这个程序将被内置到web服务器本身.这种方法能够快一点的执行但是这个编译方法有更多的难度(以后的维护).
释放这个软件包后将mod_security.c 这个文件复制到Apache源代码目录src/modeules/extra 中.
更改configureation 脚本激活这个新模块:
# ./configure /
--activate-module=src/modules/extra/mod_security /
-–enable-module=security
正常的编译,安装和启动你的web服务器.
在一些环境下,你想要安装二进制的模块.此刻我只是建立了Windows 二进制版本在下载的地方.在每一个版本中有两个DSO库文件,在每一个主要的Apache的分支中.在这些文件中选择一个适合你的Apache 的版本.参考下列描述:
复制mod_security.so (Unix 系统) 或者mod_security.dll (Windows 系统)到libexec/目录下(这个文件架是Apache 安装的目录,不是源代码目录).添加下面内容到httpd.conf :
LoadModule security_module libexec/mod_security.so
根据你现在的配置文件(你需要确认你的配置文件中模块的加载顺序)他需要使用AddModule 指令来激活模块.指令如下:
AddModule mod_security.c
在大多数的时候他不需要添加这个在配置文件中.但是推荐你将mod_security 加入到最后的module链表中(但是在实际当中,如果你打算使用内部的chroot功能的话必须要添加这个在配置文件中).可以在”在chroot 环境下模块顺序要求(Apache 1.x)”有详细的介绍.
复制mod_security.so (Unix系统)或者mod_security.dl (Windows 系统)到modules/ 目录中(这个目录是Apache 的安装目录,不是源程序目录).添加下来内容到httpd.conf配置文件中.:
LoadModule security_module modules/mod_security.so
ModSecurity 配置指令可以直接被添加到你的配置文件中(典型配置是在httpd.conf文件中).但是他不一定确信这个模块被激活或者是禁止在web服务器启动的时候 它通常是将配置信息放在
# mod_security configuration directives
# ...
自从Apache允许将一组配置数据保存在一个(例如modsecurity.conf)配置文件中,然后被httpd.conf 使用Include 方法调用.:
Include conf/modsecurity.conf
过滤引擎缺省是关闭的。需要开启请求的监视的话,可以把这个指示加入配置文件中:
SetFilterEngine On
这个参数支持的值包括:
On - 分析每个请求
Off - 无动作
DynamicOnly - 只分析运行时动态生成的请求。使用这个选项可以防止Web服务器使用宝贵的CPU资源检查对静态文件的请求。希望了解ModSecurity如何判断哪些请求是动态生成的,可以参考“选择日志中记录什么”。
请求体的有效载荷(或者POST有效载荷)扫描缺省是禁止的。需要使用的话,需要手工打开:
SetFilterScanPOST On
mod_security 对请求体支持两种编码方式:
application/x-www-form-urlencoded - 用来传输表单数据
multipart/form-data - 用于文件传输
其他的编码没有被大多数web应用程序用到。为保证只有使用这两种编码方式的请求被web服务器接受,需要在配置文件中加入下列行:
SecFilterSelective HTTP_Content-Type /
"!(^$|^application/x-www-form-urlencoded$|^multipart/formdata;)"
可以关闭基于每个request的POST有效载荷扫描。如果环境变量MODSEC_NOPOSTBUFFERING被定义,ModSecurity不会进行POST有效载荷扫描。例如,要关闭文件上传的POST有效载荷的缓存,可以:
SetEnvIfNoCase Content-Type /
"^multipart/form-data;" "MODSEC_NOPOSTBUFFERING=Do not buffer
file uploads"
对变量MODSEC_NOPOSTBUFFERING的赋值会写在debug日志里,因此可以在这里记录你关闭缓存的原因。
HTTP协议支持一种在事先不知道有效载荷大小的情况下传输请求的方法。请求体会包装成大小固定的块传输。这就是“块状传输编码”名字的由来。ModSecurity目前不支持分快请求。当一个请求按块状编码的时候,它的请求体会被忽略。到目前我所知的浏览器还没有使用块状编码来传输请求的。尽管Apache对某些操作支持这种编码,很多模块并不支持。
如果不加注意,这可能会为攻击者提供机会来散布恶意的有效载荷。可以在配置文件中加入下面这行来阻止攻击者利用这个漏斗:
SecFilterSelective HTTP_Transfer-Encoding "!^$"
这个改动不会影响使用块状传输编码来发送反馈。
只要一个请求能够匹配一条规则,一个或多个动作就会发生。每个单独的过滤器都可以有自己的动作,但是为所有的过滤器定义一套缺省的动作会更方便。(如果需要你总可以定义pre-rule动作。)你可以用SecFilterDefaultAction这个配置指示来定义缺省动作。比如,下面这句会配置引擎使之记录每一条匹配的规则,然后用状态码404来拒绝请求:
SecFilterDefaultAction "deny,log,status:404"
SecFilterDefaultAction指示值接受一个参数,一个逗号分隔的动作列表。在这里指定的动作会作用在每一个匹配的过滤器上,除非这个规则自己已经定义了自己的动作列表。
在 1.8.6 中,如果你指定了一个非致命的缺省动作列表(一个不会导致请求被拒绝的类表,比如log, pass)这样的列表在初始化阶段会被忽略。初始化阶段是被设计用来收集请求的信息的。允许非致命的动作会导致请求的部分的某些片断的丢失。因为这些信息在内部处理的时候是必需的,这样的动作不被允许。如果你希望ModeSecurity在“监测”模式下工作,需要禁止所有的隐式校验(URL编码校验,Unicode编码校验,cookie格式校验,字节范围限制)。
在 1.8.6 中,隐式请求校验(如果配置了的话)只在请求处理开始阶段被采用。在那之后,只有标准化动作会被采用。这个修改允许更好的防护(请求报头现在会在开始时被自动检查)。同时允许“监测”模式的规则。
在父目录中定义的过滤器通常会被嵌套的Apache配置所继承。这个行为通常是可接受的(而且是必需的)。但并不总是这样。有时候你需要在网站的某些部分放松检查。使用SecFilterInheritance指示
SecFilterInheritance Off
可以通知ModeSecurity来忽视父过滤器这样你可以从报头开始定义规则。这个指示只影响规则。配置总是从父上下文中继承,但是你可以使用合适的配置指示来覆盖继承来的设置。
配置和规则的继承缺省总是激活的。如果你在一个禁止的继承的配置上下文下面有另一配置上下文,你需要继续禁止继承,那么必须显式的再禁止一次。
特殊字符在通过URL传输之前需要被编码。任何一个字符都可以用%XY这种方式来编码。XY是一个十六进制的字符编码(参考http://www.rfc-editor.org/rfc/rfc1738.txt获得更多细节)十六进制数字只允许字母A到F,但是攻击者有时使用其他字母来欺骗解码算法。ModSecurity检查所有的编码,校验他们是否合法。
你可以通过下列方法打开URL编码校验功能:
SecFilterCheckURLEncoding On
在使用multipart/form-data编码(文件上传)的时候,这个指示并不检查POST的有效载荷。因为URL编码不在这种编码中使用,所以这样做不是必要的。
和其他很多的特性一样,Unicode编码校验缺省是禁止的。如果你的应用程序或是操作系统接受/理解Unicode,你应该开启这个选项。
关于Unicode和UTF-8编码的更多细节可以在RFC2279(http://www.ietf.org/rfc/rfc2279.txt)中找到。
SecFilterCheckUnicodeEncoding On
这个特性假定使用的是UTF-8编码并检查3种类型的错误:
缺失字节。 UTF-8支持2字节到6字节的编码。ModeSecurity可以定位一个或多个字节的缺失。
非法编码。大多数字符中的两个最重要的位被假定固定为0x80。攻击者利用这个来破坏Unicode解码器。
超长字符。ASCII字符可以直接映射到Unicode编码空间中,表示成单个字符。但是,多数的ASCII字符以可以表示成2到6个字符。这样可以欺骗解码器使之误认为这个字符是其他的字符(从而躲过安全检查)。
你可以强制要求HTTP请求中只包含某个特定字节范围内的字节。这对防止栈溢出攻击是有用的(因为他们通常包含一些“随机”的二进制内容)。
可以用下列指示,限制字节范围从32到126(包含这两个边界值):
SecFilterForceByteRange 32 126
缺省的范围值是0到255,也就是所有的字节值都是被允许的。
|
通常攻击者无法分辨出是否web server在运行mod_security。你可以通过发送特殊信息或者使用不寻常的HTTP代码(比如406)来出卖你自己。如果希望保持隐藏状态,那么最好是发送返回码500,意思是“Internal Server Error”。收到这个返回值的攻击者通常会认为你的应用程序已经垮掉了。因为脆弱的应用程序在遇到意料外的输入的时候经常会垮掉,ModSecutiry仍然激活这件事情对于攻击者来说仍然是不可见的。
在这个问题上有另外一派人认为,你不应该隐藏你在使用ModeSecurity的事实。理论是如果攻击者看到这个的话,会了解你已经下了功夫,攻击会变得非常困难。这样他们就会离开去寻找更脆弱些的目标。或者可能他们会觉得更有挑战性,从而更坚定的攻击。
缺省情况下Apache会对每个请求返回自身的信息。ModSecurity会保持沉默(这是推荐的状态)。但是你可以通过加上下面这行来让其他人看到ModeSecurity:
SecServerResponseToken On
结果会大概是这个样子:
[ivanr@wkx conf]$ telnet 0 8080
Trying 0.0.0 .0...
Connected to 0.
Escape character is '^]'.
GET / HTTP/1.0
HTTP/1.1 406 Not Acceptable
Date: Mon, 19 May 2003 18:13:51 GMT
Server: Apache/ 2.0.45 (Unix) mod_security/1.5
Content-Length: 351
Connection: close
Content-Type: text/html; charset=iso-8859-1
Not Acceptable
An appropriate representation of the requested resource /
could not be found on this server.
Apache/ 2.0.45 (Unix) mod_security/1.4.2 Server at
wkx.dyndns.org Port 80
Connection closed by foreign host.
你应该注意Apache本身支持两个运行时指示:ServerTokens和ServerSignature。使用这些指示你可以彻底隐藏你的服务器的消息,无论ModSecurity是如何配置的。
使用指示SecFilterDebugLog可以选择一个存储调试信息的文件。如果参数不是以“/”开头,Apache的home路径会添加在参数的前面。
SecFilterDebugLog logs/modsec_debug_log
你可以用SecFilterDebugLevel控制调试信息细致到何种程度:
SecFilterDebugLevel 4
可能的日志控制级别包括:
0 - 没有信息
1 - 重大事件(在error_log中也会记录)
2 - 普通信息
3 - 更详细的信息
ModSecurity内部使用调试信息级别最高到9,但是只用于ModSecurity开发者调试用。
当过滤引擎被激活的情况下,每个进入的请求在被处理前都要被解释和分析。分析会以一系列事先设计用来校验请求格式的内置检查作为开始。这些检查可以通过配置指示来控制。在第二个阶段,请求会通过一系列的和这个请求相匹配的用户定义的过滤器。每当发生一个肯定的匹配,特定的动作会被执行。
最简单的规律形式是“简单”形式。看上去是这样的:
SecFilter KEYWORD
象这样的每一个简单过滤器,ModSecurity会在请求中查找关键字。搜索范围很松,会应用在请求的第一行上(象这个样子GET /index.php?parameter=value HTTP/1.0)。对POST请求来说,请求体也会被搜索(当让是在请求体缓存被激活的前提下)。
过滤器不是作用在原始的请求数据上的,而是在一个标准化了的副本上。这样做是因为攻击者可以(而且正在)通过使用不同的躲避技术来逃过监测。比如,你可能希望通过创建一个过滤器来监测shell命令的执行
SecFilter /bin/sh
但是攻击者可以使用一个字符串:/bin/./sh(具有相同的含义)来逃过过滤器。
ModSecurity自动采用下列转换:
l 只对Windows平台生效,/转换成/
l 把/./缩减为/
l 把//缩减为/
l 解码使用URL编码过的字符
你可以选择激活或禁止下列检查:
l 检验URL编码
l 只允许使用指定范围内的字节
在ModeSecurity当前版本中实现的标准化有时候会影响你的正常工作。你可能在写一个在其他地方检查URI的信号的时候遇到很大困难。这是因为URI在请求中的形式是“http://” ,而这个字符串会在标准化过程中翻译成“http:/”。注意在后面这个版本中只有一个斜线。
空字节攻击会迷惑C/C++开发的程序,是它们误认为一个字符串在实际的结束位置之前就结束。这种类型的攻击通常会被合理设置的SecFilterByteRange过滤器拒绝掉。但是如果你没有这样做,空字节会干扰ModSecurity的处理。为了击败这类攻击,ModSecurity在解码阶段搜索空字节并把他们转换成空格。因此,在以前的版本这个过滤器
SecFilter hidden
不会检测到下列请求中的单词hidden
GET /one/two/three?p=visible%00hidden HTTP/1.0
但是在现在的版本是可以检测到的。
我们前面讨论的最简单的过滤器实际上要稍微复杂一点。它的完整的语法是这样的:
SecFilter KEYWORD [ACTIONS]
首先,KEYWORD不是一个普通单词,而是一个正则表达式。正则表达式是设计用来在文本中匹配模式的迷你编程语言。为了充分理解这个强大的工具,需要先充分理解正则表达式。我推荐从下面的资源之一入手:
l Perl兼容的正则表达式的手册页:http://www.pcre.org/pcre.txt
l Perl正则表达式: http://www.perldoc.com/perl5.6/pod/perlre.html
l 精通正则表达式:http://www.oreilly.com/catalog/regex
l Google搜索“正则表达式”:http://www.google.com/search?q=regular%20expressions
要小心(A|B)+类型的正则表达式,在海量的文本上使用这样的表达式会在栈尺寸比较小的平台,比如Windows上导致栈溢出。栈溢出会导致无法不可回复的段错误。
第二个参数是动作列表的定义,用来指定当过滤器匹配成功是发生的动作。关于动作会在后面解释。
如果一个正则表达式的第一个字符是惊叹号,过滤器会反转这个表达式。比如下面的例子:
SecFilter !php
会拒绝所有的不包含单词php的请求。
所以SecFilter可以让你快速上手,但是你很快会发现它的搜索面太宽,工作的不是很好。另一个指示:
SecFilterSelective LOCATION KEYWORD [ACTIONS]
让你可以准确的选择在哪里进行搜索。
KEYWORD和ACTIONS设置和SedFilter是一样的。LOCATION需要解释一下。LOCATION参数包含用管道符分开的一系列的位置标识符。看一下下边的例子:
SecFilterSelective "REMOTE_ADDR|REMOTE_HOST" KEYWORD
只在客户端的IP地址和主机名上使用正则表达式。可能的位置标识符包括所有的CGI变量和其他的一些内容。下面是完整的列表:
l REMOTE_ADDR
l REMOTE_HOST
l REMOTE_USER
l REMOTE_IDENT
l REQUEST_METHOD
l SCRIPT_FILENAME
l PATH_INFO
l QUERY_STRING
l AUTH_TYPE
l DOCUMENT_ROOT
l SERVER_ADMIN
l SERVER_NAME
l SERVER_ADDR
l SERVER_PORT
l SERVER_PROTOCOL
l SERVER_SOFTWARE
l TIME_YEAR
l TIME_MON
l TIME_DAY
l TIME_HOUR
l TIME_MIN
l TIME_SEC
l TIME_WDAY
l TIME
l API_VERSION
l THE_REQUEST
l REQUEST_URI
l REQUEST_FILENAME
l IS_SUBREQ
还有一些特殊的位置:
POST_PAYLOAD – filter the body of the POST request
ARGS - filter arguments, the same as QUERY_STRING|POST_PAYLOAD
ARGS_NAMES – variable/parameter names only
ARGS_VALUES – variable/parameter values only
COOKIES_NAMES - cookie names only
COOKIES_VALUES - cookie values only
甚至还有更特殊的:
HTTP_header – search request header header
ENV_variable – search environment variable variable
ARG_variable – search request variable/parameter variable
COOKIE_name - search cookie with name name
位置标识符ARG_variable在和ARG位置合并使用的时候支持反转。例如:
SecFilterSelective "ARGS|!ARG_param" KEYWORD
会搜索除了名字是“param”的参数之外的所有的参数。
。
ModSecurity 对Cookies提供完全支持。默认情况下,版本 0 的Cookies被使用 (Netscape 类型的cookies, Netscape-style cookies)。然而,ModSecurity 同时也支持版本 1 的Cookies(RFC 2965 中定义的Cookies). 你可以使用以下的格式,定义支持版本 1 的cookies:
# enable version 1 (RFC 2965) cookies
SecFilterCookieFormat 1
默认情况下,ModSecurity不会去尝试标准化cookie的名称及值。 然而,一些应用程序或者平台(例如:PHP)会对cookeie编码,以便你可以选择为cookie添加标准化技术。你可以通过对SecFilterNormalizeCookies指令的定义实现:
SecFilterNormalizeCookies On
在ModSesurity 1.8.7 版本以前支持SecFilterCheckCookieFormat指令。在1.8.7以后的新版本中这条指令已经被取消。这条指令可以仍然用在配置文件中,但是不起任何作用。在1.9.X以后的版本中这条指令已经被完全取消。
MoSecurity 在Apache 2版本中支持输出过滤。默认情况下,输出过滤是被禁止的,如果你想要使用必需首先打开这个选项:
SecFilterScanOutput On
之后,使用特殊变量OUTPUT简单添加选择过滤器:
SecFilterSelective OUTPUT "credit card numbers"
在http://www.webkreator.com/php/许多和我一起研究这些朋友知道我对防止PHP致命错误( fatal errors)有些无能为力。我花了很长时间去研究防止PHP的致命错误信息泄漏给用户。 (×××)(参考:http://www.webkreator.com/php/configuration/handling-fatal-and-parse-errors.html)
但是现在,最终在这方面我不用太担心了。以下的指令可以从返回信息(response)的主体文件中捕获PHP输出错误,而不是从单独的错误回应(response)中捕获,并且执行定制的PHP脚本(同时通报应用程序管理员)。
SecFilterSelective OUTPUT "Fatal error:" deny,status:500
ErrorDocument 500 /php-fatal-error.html
你应该注意到尽管你可以混合输入输出过滤器,但是它们不能在同一时间执行。输入过滤器在一个请求被Apache处理之前被执行。输出过滤器在Apache完成请求处理以后被执行。
动作(Actions)skipnext 和 chain不能在输出(output)过滤器工作。输出过滤仅仅用于纯文本和HTML输出的过滤。添加常规的二进制内容的表达式(例如:Image)将仅仅会使服务器慢下来(~~~~~~).ModSecurity 能基于它的mime类型有选择的添加输出过滤给响应(responses)。使用SecFilterOutputMimeTypes指令你能告诉它哪一个mime 类型去监视:
SecFilterOutputMimeTypes "(null) text/html text/plain"
上面的一个简单ModScurity的配置实例会给纯文本文件、HTML文件,、和mime类型不是被特殊的"(null)"位置的文件添加输出过滤。使用输出缓冲将使ModSecurity 保持完整的内存中的页输出,无论它本身有多大。内存开销是页长度的2倍以上。
有以下几种动作:
主动作(action)将生成一个决策决定是否去继续请求或者拒绝请求。有且只有一个主动作存在。如果你在parameter里设置了几个主动作,最后一个动作将被执行。主动作为deny,
pass, 和 redirect(转向).
第二动作将由主动作在决策中作定义,并在一个独立匹配的过滤器(filter)中被执行。在第二动作中可以是任何值,例如:exec 是一个第二动作。
下面的流动作能改变相应的规则的流向,因为过滤引擎能跳到其他规则,或者跳到一个或几个规则。实现动作是chain 和 skip.
参数不是真正的动作,而是一个针对过滤器的附加参数的方法(method)。有些参数能被用于真正的动作。例如:status 给主动作deny提供一个响应编码。
你可以把动作放在三个地方。一个是SecFilterDefaultAction指令,在这里你可以定义你想在大多数过滤匹配中被执行的动作:
SecFilterDefaultAction "deny,log,status:500"
这个例子定义了一个由三个动作组成的动作列表。逗号用与在列表中分开动作。开始的两个动作是由单个的单词组成。但是第三个动作需要一个参数,用冒号把参数和动作名分隔开。
你也能指定过滤器预处理动作。做为一个可选参数,有两个过滤器指令可以接受一个指定动作的设置(SecFilter 和SecFilterSelective)。当使用的过滤器预处理动作是一个伪装请求时,这个请求在过滤匹配器中将会被拒绝。如果你想容许请求继续,你必须明确的设置一个non-fatal动作(例如:“log,pass”.)
对于 1.8.6 ,如果你指定了non-fatal为默认动作(例如:“log,pass”.),那么在mod_security初始化的阶段它就会被忽略。初始化阶段仅仅用来收集关于请求的信息,容许non-fatal动作是因为一些请求块错误(在mod_security中对于内部处理)。因此,如果你想mod_security在探测"detect-only"模式下运行,你应该禁止所有固有确认(检查 URL encoding, Unicode, cookie 格式化, byterange).
容许在过滤匹配中请求被继续。这个动作用在当你想纪录一个匹配(match)而不并不想执行其他的动作时候。
SecFilter KEYWORD "log,pass"
在这个动作执行后,请求将被容许通过,并且没有别的过滤器被重试。
# stop filter processing for request coming from
# the administrator's workstation
SecFilterSelective REMOTE_ADDR "^192.168.2.99$" allow
在过滤匹配中中断一个请求处理。除非status动作也在使用。ModSecurity 会立即返回一个HTTP 500错误代码。如果一个请求被拒绝,文件头mod_security-action将会被添加到请求文件头列表中去。这个文件头包括code使用状态。
当一个请求被拒绝的时候使用替代HTTP状态代码。
下面的规则:
SecFilter KEYWORD "deny,status:404"
当他被触发的时候将返回"Page not found"信息。如果在配置里有这条规则Apache 错误提示信息会被触发。所以,如果你以前已经为一个设定的状态定义了一个定制的错误页,那么它将会被执行并且给用户显示。
在过滤匹配中可以重定向用户到指定的URL.例如:
SecFilter KEYWORD "redirect:http://www.modsecurity.org"
这个配置指令不考虑HTTP状态代码或者deny关键字。注意,URL中不能包括逗号。
在过滤匹配中执行二进制文件,需要在指令中添加要执行的文件的完整路径,例如:
SecFilter KEYWORD "exec:/home/ivanr/report-attack.pl"
如果这条指令存在,它对主动作不产生影响。这个动作总是调用不带参数的脚本,但是提供所有在环境中的信息。所有的通用的CGI环境变量都将放在这里。你可以在预过滤匹配中执行一个二进制文件。完成以后将增加一个mod_security-executed的报头到请求包列表中。你应该理解调用线程会导致在所有的线程将会复制到新的线程。在多进程选项中调用会导致更大的开销。
日志过滤用于纪录Apache的错误日志。
不纪录Apache的错误日志。
这个动作容许你跳过一个或者多条规则。当你设定在特殊情况下,不需要完成一些校验,你可以用这个动作。默认情况下,这个动作会跳过下一条规则。如果你指定了可选参数,它能跳过任何规则的数目。
SecFilterSelective ARG_p value1 skipnext:2
SecFilterSelective ARG_p value2
SecFilterSelective ARG_p value3
链接规则容许你将几条规则链接到到一起。只有在链接中的最后一条规则会影响请求,在它前面的所有的规则也必须被匹配。
在这里举一个例子:
我想限定管理员用户只能从指定的IP地址登陆。但是,管理员登陆界面是和别的用户共享的,并且我不能用标准的Apache界面。因此,我用了这两条规则:
SecFilterSelective ARG_username admin chain
SecFilterSelective REMOTE_ADDR "!^YOUR_IP_ADDRESS_HERE$"
如果参数username存在并且它的值是admin那么第一条规则匹配。之后第二条规则被执行,并且它会尝试用远程请求地址和单独的IP地址匹配。如果请求地址不匹配(注意:以!做为开始),则请求被决绝。
在响应请求之前可以暂停几毫秒。这对于减速或者完全拒绝一些web扫描是非常有用的。一些扫描软件如果暂停过长它就直接放弃扫描。
在一种情况使用这个选项要特别的小心。每一个web服务器的安装配置是限定的,在任何时候它都需要提供最大请求数。如果弱扫描软件是并行执行请求,使用这个选项有一个较长延迟时间,可以创建一个“voluntary”的拒绝服务攻击。
只要有可能,ModSecurity会在请求报头中增加信息,从而容许你的脚本发现并使用它。显然,为了你的脚本能够被执行,你必须配置ModSecurity避免它拒绝请求。乍看起来,可能有点奇怪我使用请求报头,而不是其他的手段,比如环境变量,来达到这个目的。虽然使用环境变量看起来更规范一些,输入报头对于使用ErrorDocument指示(见下文)来执行的脚本来说总是可见的,凡是对于环境变量则不然。
这是添加的报头的列表:
a) mod_security-executed; 被执行的二进制文件的路径
b) mod_security-action; 返回的状态码
c) mod_security-message; 监测到的错误信息,和记录在error_log中的信息相同。
如果你的配置返回了HTTP状态码500,而且你配置Apache使其是要在这个状态出现时都执行一个自定义的脚本(比如ErrorDocument 500 / error500.php)来使你能够使用自己喜欢的脚本引擎来相应这些错误。关于错误的信息会放在环境变量REDIRECT_*和HTTPD_MOD_SECURITY_*里(正如这里描述的:http://httpd.apache.org/docs-2.0/customerror.
html)。
某些情况下,检测到一个显著的危险攻击或是一系列攻击后,你会希望组织同一个来源的进一步的攻击。你可以通过修改防火墙使其拒绝从某一特定的IP地址发出的全部流量来做到这一点(我在iptables的基础上写了一个助手脚本,可以在这里下载:http://www.apachesecurity.net)。
这个方法可能非常危险因为它会导致拒绝服务(DoS)攻击。比如,一个攻击者可以使用一个代理来发起攻击。拒绝来自一个代理服务器的所有请求是很危险的事,因为所有合法的用户也会收到影响。
因为多数代理会发送描述原始客户端的信息(可以在下面的网址找到有关的信息:http://www.webkreator.com/cms/view.php/1685.html,在“Stop Hijacking”一节),我们可以试着聪明一点,找到真正的IP地址。虽然这样能工作,但是考虑下面的情况:
l 攻击者在直接访问应用程序,但是伪装成一个代理服务器,引用一个随机的(或者合法的)IP地址作为“真实的”源IP地址。如果我们基于推断出的信息开始拒绝请求,攻击者会简单的替换IP地址然后继续攻击。结果是我们禁止了合法的用户,但是攻击者仍然在自由地寻找应用程序地漏洞。
所以只有在下列情况下有用:你不允许通过代理访问你的应用程序,或者只允许通过广为人知的,更重要的,可信任的代理访问。
如果你仍然希望基于IP地址来禁止请求(不理会我们所有的警告),你需要写一个小的脚本,在过滤器匹配的时候执行。这个脚本应该从环境变量里取出攻击者的IP地址,然后调用iptables或者ipchains来禁止这些IP地址。在mod_security以后的版本中我们将会包含一个这样的脚本示例。
ModSecurity支持multipart/form-data编码用于文件上传
ModSecurity总是上传文件到临时目录。你可以用SecUploadDir指示选择目录:
SecUploadDir /tmp
最好选择一个私有文件夹存放文件,只允许启动web服务器的用户访问。否则,其他的服务器用户可能访问通过web服务器上传的文件。
你可以选择执行一个外部脚本在允许文件通过web服务器到达应用程序之前校验这个文件。SecUploadApproveScript指示提供了这个功能。如下例所示:
SecUploadApproveScript /full/path/to/the/script.sh
一个命令行参数会提供给脚本——正在被上传的文件的完整路径。脚本可以对文件作任何操作。处理完之后,响应结果会输出到标准输出。如果相应结果的第一个字符是“1”,文件将会被接受。如果是其他的结果,(上传文件的)请求将会被拒绝。你的脚本可以用响应结果来提供描述性更强的错误信息。这个信息会被输出到debug log。
你可以选择保存通过web服务器上传的文件。只要在你的配置中添加下列行就可以了:
SecUploadKeepFiles On
文件会被存放在SecUploadDir指示指定的目录中。
Apache 1.x没有提供一个合适的底层架构用来作请求侦听。除非把请求整个放在内存中才能侦听。Apache 1.x提供一个选择,分析内存中的multipart/form-data(文件上传)请求,或者根本不分析(选择性的关闭POST处理)。
但是,使用Apache 2.x,你可以定义用来解析内存中存放的multipart/form-data的内存数量。当一个请求大于你允许的数量的时候,会使用一个临时文件。缺省值是60KB,但是可以通过SecUploadInMemoryLimit指示来改变:
SecUploadInMemoryLimit 125000
很多web应用程序防火墙有一个困难的工作——在对应用程序和商业逻辑完全不了解的情况下,搞清经过的数据的意思。他们提供的保护来源于在外部拥有的一个独立安全层。因为数据确认作了两次,可以在不触动应用程序的前提下增加安全性。但是,在某些情况下,每件事情都作两次会带来问题。在通信协议没有很好定义的地方,或者在设备或应用程序在做一些规范中没有定义的事情。
最糟糕的违规者是cookie规范。(实际上是所有这4个定义:http://wp.netscape.com/newsref/std/cookie_spec.html, http://www.ietf.org/rfc/rfc2109.txt,
http://www.ietf.org/rfc/rfc2964.txt,http://www.ietf.org/rfc/rfc2965.txt)。因为很多真实的生活可能发生的情况,在这个规范中没有定义——留给程序员去做任何他们认为适宜的事情。大多数情况这不是问题,如果cookie是结构良好的话——事实上多数情况下是的。问题不明显还因为多数应用程序解析的cookie都是这些应用程序自己发送的。如果你从web应用程序防火墙的角度观察,当一个确定的对手试图突破它的时候,这就是问题了。
我会用一个例子来解释。
ModSecurity的1.8.x分支,直至 1.8.6 (我在1.8.7中做了改进)使用了一个cookie解析器。当我写这个解析器的时候,我觉得它真的很好,因为它能够处理v0和v1的cookie。但是,我犯了一个错误,没有象一个攻击者那样去思考。正如Stefan Esser向我指出的,在v0和v2之间的格式差异可能被利用,使得一个v1的解析器看到一个cookie,但是一个v0的解析器看到更多。就像这样:
Cookie: innocent=”; nasty=payload; third=”
V0的解析器不识别双引号。它只寻找分号然后分割报头。一个这样的解析器看到的多个cookie:“innocent”,“nasty”,“third”。另一方面,V1的解析器只看到一个cookie——“innocent”。
阻抗不匹配是如何影响web应用程序防火墙的用户和开发者的呢?它当然会让生活变得复杂,但是这没关系——这是游戏的一部分。开发者必须合并比较好和比较快的解析程序。例如,ModSecurity 1.8.7 包含两个cookie解析器。用户可以选择任一使用(默认使用v0的解析器)。但是因为不能自动化,这样的改进只是使防火墙更加复杂——又多了一件需要用户思考和配置的东西。
另一个方面,如果不想考虑cookie解析器的问题,用户总可以后退回去使用HTTP中那些定义的更好的部分。例如,用户可以使用HTTP_Cookie来定位整个cookie报头,而不是用Cookie_innocent来定位一个单独的cookie。其他变量,比如ARGS,可以看到全部的变量,不管对手多么努力的掩饰他们。
一个常用的减缓和迷惑攻击者的技术就是改变web服务器的标识。Web服务器通常会在每个HTTP响应的Server报头中包含服务器的标识。Apache在这个方面更加明显,缺省情况下不只发送完整的名字和版本号,而且还允许服务器模块把它们的版本也附加上。
为了改变Apache web服务器的标识,你可能不得不查看源码,会发现“Apache”这个名字是硬编码的,然后改变它,重新编译服务器。你可以使用SecServerSignature指示达到同样的效果:
SecServerSignature "Microsoft-IIS/5.0"
需要注意的是,虽然这个方法工作的很好,但是熟练的攻击者(和工具)可能使用其他的技术来对web服务器进行“指纹识别”。例如,缺省文件、错误信息、发出报头的顺序、服务器响应特定请求的方式和其他类似的方法——都能泄漏真正的标识。我会在mod_security的将来的发行版中查看进一步的对标识隐藏的改进。
如果你改变了Apache的签名,但是被错误日志中奇怪的信息打扰(一些模块仍然可见——这个只影响错误日志,从外部看它仍然如期望的工作):
[Fri Jun 11 04:02:28 2004] [notice] Microsoft-IIS/5.0
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 "
SecFilter "<.+>"
第一个过滤器只针对使用标签的Javascript注入进行保护。第二个更通用,不允许参数中存在任何HTML
使用这个过滤器的时候要谨慎,因为很多应用程序需要参数中包含HTML(例如,CMS应用程序,论坛等等)。这种情况下你可以使用选择性过滤器。比如,你可以使用第二个过滤器作为全站的规则,但是之后可以用下面的代码放宽对特定脚本的规则:
SecFilterInheritance Off
# other filters here ...
SecFilterSelective "ARGS|!ARG_body" "<.+>"
这段代码片段只接受命名参数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 "!^[/x 0a /x0d/x20-/x 7f ]+$"
从1.8开始,可以使用Apache自定义日志专门记录那些和mod_security有关的请求。这是因为ModSecurity现在只要采取了动作,就定义一个环境变量mod_security-relevant。要使用自定义的日志文件,在配置中添加下边的(或相似的)内容:
CustomLog logs/modsec_custom_log /
"%h %l %u %t /"%r/" %>s %b %{mod_security-message}i" /
env=mod_security-relevant
如果你希望追溯某个特定的用户或者攻击者的操作步骤的时候,标准的Apache日志用处不大。症结在于只有每个请求的一个很小的子集被记录在日志文件里。这个问题可以用ModSecurity的审计日志功能来补救。这两个指示:
SecAuditEngine On
SecAuditLog logs/audit_log
会通知ModSecurity你想要一个完整的审计日志存放在日志文件audit_log里。下边是一个被记录的请求的例子:
========================================
Request: 192.168.0.2 - - [[18/May/2003:11:20:43 +0100]] "GET /
cgi-bin/printenv?p1=666 HTTP/1.0" 406 822
Handler: cgi-script
----------------------------------------
GET /cgi-bin/printenv?p1=666 HTTP/1.0
Host: wkx.dyndns.org:8080
User-Agent: mod_security regression test utility
Connection: Close
mod_security-message: Access denied with code 406. Pattern
match "666" at ARGS_SELECTIVE.
mod_security-action: 406
HTTP/1.0 406 Not Acceptable
========================================
你可以看到在第一行你可以得到你平常从Apache得到的(记录)。第二行包含假定用来处理请求的处理程序的名字。完整的请求(包括额外的mod_security头信息)出现在分隔符后,响应报头(在这个例子中只有一行)出现在空白行后。当POST过滤打开的时候,POST有效载荷始终会被包含在审计日志里。实际的响应永远不会被包含(至少在这个版本里不会)。
这时,模块的审计日志部分会记录Apache 1.x的错误信息,在Handler:行的下边一行。这行总是以Error:开头。如果可能,这个功能会被添加到这个模块的Apache 2.x版本里。
如果你在Apache的配置中添加了mod_unique_id,mod_security会监测它并使用它产生的环境变量(UNIQUE_ID)。它的值会被写到审计日志里。你可以把这个唯一标识符写在显示给用户的错误页面上,以后用它来跟踪和修复假阳性(false positive,不知道怎么翻译,感觉是作者笔误)。
参数SecAuditEngine接受下列四个值之一:
l On —— 记录所有请求
l Offi —— 完全不记录请求
l RelevantOnly —— 只记录相关请求,相关请求是那些会能匹配过滤器的请求
l DynamicOrRelevant —— 记录动态生成的或者相关的请求。如果一个请求的处理机(handler)不是null,那么它是动态的。
让ModSecurity记录动态请求有时候需要一些工作,视你的配置而定。在Apache的理论中,对请求的响应是由所谓的处理机(handler)来生成的。如果一个请求附加了处理机,那么它被看作是动态的。当时,实际情况中,Apache可以被配置来提供没有处理机的动态页面(Apache会基于资源的MIME类型来选择模块)。例如,如果你在主分发版内(?)如下介绍的配置PHP,上述情况就会发生:
AddType application/x-httpd-php .php
虽然这样也能工作,它并不是完全正确的。但是如果你用下列内容替换上边的那行:
AddHandler application/x-httpd-php .php
PHP也会工作,Apache将有一个分配给这类请求的处理机,而且审计日志记录器可以能够选择性的记录日志。
一个小型的HTTP测试工具作为ModSecurity的一部分被开发出来。它提供简易的方法来发送手工制作的HTTP请求到服务器,判断攻击是否被成功监测到。
不加参数调用这个工具会打印它的使用方法:
$ ./run-test.pl
Usage: ./run-test.pl host[:port] testfile1, testfile2, ...
第一个参数是服务器的主机名,端口是可选的。其他所有的参数是包含手工制作的HTTP请求的文件的名字。
为了让你的生活轻松一些,这个工具会自动的生成某些请求报头:
l Host: hostname
l User-Agent: mod_security regression testing utility
l Connection: Close
如果需要,你可以在请求中包含它们。如果已经有这些了,这个工具不会再添加。
这是HTTP请求看上去的样子:
# 01 Simple keyword filter
#
# mod_security is configured not to allow
# the "/cgi-bin/keyword" pattern
#
GET /cgi-bin/keyword HTTP/1.0
这个请求只包含一行,没有另外的报头。你可以创建任意复杂的请求。这是一个POST用法的例子:
# 10 Keyword in POST
#
POST /cgi-bin/printenv HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 5
p=333
文件首部的以#开头的几行会被当作注释。第一行比较特殊,它应该包含测试的名字。
这个工具期望返回值是状态200,如果你希望其他的响应,你需要在第一行(行内的任何位置)写上期望的响应码来通知测试工具。就象这样:
# 14 Redirect action (requires 302)
GET /cgi-bin/test.cgi?p=xxx HTTP/1.0
括号和“requires”关键字不是必需的,但是为了更好的可读性还是推荐使用。
当编写用来保护PHP应用程序的ModSecurity规则的时候,需要留意PHP的一些特性。很容易设计一个在你攻击自己的时候有效,对攻击的变种则完全无效的规则。下边是我注意到的一些问题:
l 当register_globals被设置成On的时候,请求参数变成了全局变量。(在PHP 4.x中甚至可能覆盖GLOBALS数组)
l Cookie被当作请求参数处理
l 参数开头的空格会被忽略
l 其他的空格(参数名称中的)会被转换成下划线
l 从请求和环境变量中取参数的顺序是EGPCS(环境变量,get,post,cookies,内置变量)。这意味着POST参数会覆盖通过请求行传输的参数(在QUERY_STRING里)。
l 当magic_quotes_gpc被设置成On的时候,PHP会使用反斜线(“/”)转义下列字符:单引号、双引号、反斜线和NULL。
l 如果magic_quotes_sybase被设置成On,只有单引号会被转义(用另一个单引号)。这种情况下magic_quotes_gpc的设置就无关了(不起作用了?)。
正则表达式非常强大。这个例子可以检查一个参数是否是在0到99999之间的整数:
SecFilterSelective ARG_parameter "!^[0-9]{1,5}$"
禁止对一个应用程序上传文件,但是可以上传到一个子目录里:
# Reject requests with header "Content-Type" set
# to "multipart/form-data"
SecFilterSelective HTTP_CONTENT_TYPE multipart/form-data
# Only for the script that performs upload
# Do not inherit filters from the parent folder
SecFilterInheritance Off
早期版本的FormMail可能被滥用,发送email给任意收件人(我被告知有新版可以避免这个问题)。
# Only for the FormMail script
# Reject request where the value of parameter "recipient"
# does not end with "@webkreator.com"
SecFilterSelective ARG_recipient "!@webkreator.com$">
ModSecurity提供的保护是有代价的。你的web服务器会变的有一点点慢,并使用更多的内存。
根据我的经验,速度差异并不显著。我在开发早期做了一些实验,速度差异大约是10%。但是,如果你配置ModSecurity只处理动态请求,差异会更小。在实际的网站中,一个对动态页面的请求通常会伴随几个对其他类型文件(CSS,JavaScript,图片)的访问。性能影响是和配置的复杂度直接相关的。你可以使用Apache 2中的性能测量模块来测量ModeSecurity在每个请求的处理上花了多少时间。在我的测试中,这个时间大约是2-4毫秒(在一个2GHz处理器的服务器上)。
为了能够分析一个请求,ModSecurity把请求数据放在内存里。多数情况下数量不大,因为多数请求都很小。但是,对网站的那些上传文件的部分可能就有问题了。为了防止这个问题,你需要对网站的这些部分关闭“请求体缓冲”。(这只是Apache 1.x版本的问题。Apache 2.x版本在请求太大以至于无法放进内存的时候会使用磁盘上的临时文件来存储。)在任何情况下,在Apache配置中检查和设置各种限制都是明智的(参考http://httpd.apache.org/docs/mod/core.html#limitrequestbody关于LimitRequestBody,LimitRequestsFields,LimitRequestFieldsize和
LimitRequestLine指示)。
调试特性很有用,但是它会为每个请求写大量的数据到文件中。因此对繁忙的服务器可能会造成瓶颈。没有理由在产品服务器上使用调试特性,所以保持关闭这个选项。
审计日志特性类似,也会因为两个原因产生瓶颈。首先,大量的数据会写到磁盘;其次,对文件的访问必须是同步的。如果你仍然希望使用审计日志,试着创建很多不同的审计日志,对服务器上运行的每个应用程序创建一个,来最小化同步的代价(这个建议在Apache 2.x版本中不能去掉任何代价,因为同步是用一个集中的互斥量来实现的)。
有一些已知的问题:
l (只针对Apache 2)ModSecurity支持响应体截获,但是它不能显式地照顾响应报头。这可能在一些原始的响应头通过的时候发生。
l (只针对Apache 2)响应体截获现在和mod_deflate不完全兼容。为了保证平滑操作,ModSecurity会阻止mod_deflate对那些(只针对那些)已经被截获的响应进行操作。
请阅读下列注意事项:
l 你应该小心地考虑你加到配置里的每个过滤器规则的影响。特别的,你肯定不希望用非常宽泛的规则拒绝访问。结果会是错误的positives(?不知道怎么翻译)和盛怒的用户。
l 尽管ModSecurity可以用在.htaccess文件里(需要AllowOverride选项),不要对你不信任的人员开放使用。
其他一些有趣的资源:
l Web Security Appliance With Apache and mod_security, a SecurityFocus article:
http://www.securityfocus.com/infocus/1739
l Introduction to mod_security, published on ONLamp.com:
http://www.onlamp.com/pub/a/apache/ 2003/11/26 /mod_security.html
下边是推荐的mod_security最小配置。这只是一个设计用来避免给你带来急性头痛的起点。你应该观察它,在可能的地方加强这个配置
# Only inspect dynamic requests
# (YOU MUST TEST TO MAKE SURE IT WORKS AS EXPECTED)
SecFilterEngine DynamicOnly
# Reject requests with status 403
SecFilterDefaultAction "deny,log,status:403"
# Some sane defaults
SecFilterScanPOST On
SecFilterCheckURLEncoding On
SecFilterCheckCookieFormat Off
SecFilterCheckUnicodeEncoding Off
# Accept almost all byte values
SecFilterForceByteRange 1 255
# Server masking is optional
# SecServerSignature "Microsoft-IIS/5.0"
SecUploadDir /tmp
SecUploadKeepFiles Off
# Only record the interesting stuff
SecAuditEngine RelevantOnly
SecAuditLog logs/audit_log
# You normally won't need debug logging
SecFilterDebugLevel 0
SecFilterDebugLog logs/modsec_debug_log
# Only accept request encodings we know how to handle
# we exclude GET requests from this because some (automated)
# clients supply "text/html" as Content-Type
SecFilterSelective REQUEST_METHOD "!^(GET|HEAD)$" chain
SecFilterSelective HTTP_Content-Type /
"!(^application/x-www-form-urlencoded$|^multipart/form-data;)"
# Require Content-Length to be provided with
# every POST request
SecFilterSelective REQUEST_METHOD "^POST$" chain
SecFilterSelective HTTP_Content-Length "^$"
# Don't accept transfer encodings we know we don't handle
# (and you don't need it anyway)
SecFilterSelective HTTP_Transfer-Encoding "!^$"
from: http://man.chinaunix.net/newsoft/ModSecurity_for_Apache/index.html