当过滤引擎被激活的情况下,每个进入的请求在被处理前都要被解释和分析。分析会以一系列事先设计用来校验请求格式的内置检查作为开始。这些检查可以通过配置指示来控制。在第二个阶段,请求会通过一系列的和这个请求相匹配的用户定义的过滤器。每当发生一个肯定的匹配,特定的动作会被执行。
最简单的规律形式是“简单”形式。看上去是这样的:
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
DOLUPAMENT_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
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”.)
对于
容许在过滤匹配中请求被继续。这个动作用在当你想纪录一个匹配(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_SELUPARITY_*里(正如这里描述的: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以后的版本中我们将会包含一个这样的脚本示例。