摘要:

本文介绍了WAF(Web Application Firewall)中的ModSecurity,它是一个用于过滤和拦截HTTP流量的WAF,通常部署在反向代理服务器中。ModSecurity通过一组规则集进行工作,并允许用户指定需要过滤的请求特征以及服务器应该采取的操作。文章提供了编译ModSecurity的步骤,包括从GitHub克隆源码并解决编译过程中的依赖问题。接下来,文章介绍了如何将ModSecurity与Nginx服务器连接起来,通过编译ModSecurity-nginx和Nginx,并配置Nginx以使用ModSecurity。最后,文章演示了如何启动ModSecurity并配置WAF过滤规则,包括指定规则集和使用自定义规则集。通过这些步骤,读者可以了解如何使用ModSecurity构建和配置WAF以保护Web应用程序。

WAF - ModSecurity

ModSecurity是一个WAF(Web Application Firewall),可以过滤并拦截Http流量,通常被部署在反向代理服务器中。

WAF通常根据一组规则集(Core Rule Set)进行工作,用户可以指定需要过滤的请求特征,并给出服务器所需进行的操作。

环境说明

编译ModSecurity

从Github克隆源码:https://github.com/SpiderLabs/ModSecurity

1
2
3
4
$ ./build.sh
$ ./configure
$ make
$ sudo make install

利用make进行编译,该项目的makefile通过./configure自动生成。在编译过程中涉及一些依赖:

  • pcre:正则表达式库
  • YAJL:Json解析库
  • LMDB:Key-Value数据库
  • GeoIP:IP与地理位置映射管理
  • libmaxminddb:地理位置数据库

可以去对应网站下载源码手动编译,也可以利用包管理器安装相关依赖。解决依赖问题后即可安装LibModSecurity。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
❯ tree
.
├── bin
│ └── modsec-rules-check
├── include
│ └── modsecurity
│ ├── actions
│ │ └── action.h
│ ├── anchored_set_variable.h
│ ├── anchored_set_variable_translation_proxy.h
│ ├── anchored_variable.h
│ ├── audit_log.h
│ ├── collection
│ │ ├── collection.h
│ │ └── collections.h
│ ├── debug_log.h
│ ├── intervention.h
│ ├── modsecurity.h
│ ├── reading_logs_via_rule_message.h
│ ├── rule.h
│ ├── rule_marker.h
│ ├── rule_message.h
│ ├── rule_unconditional.h
│ ├── rule_with_actions.h
│ ├── rule_with_operator.h
│ ├── rules.h
│ ├── rules_exceptions.h
│ ├── rules_set.h
│ ├── rules_set_phases.h
│ ├── rules_set_properties.h
│ ├── transaction.h
│ ├── variable_origin.h
│ └── variable_value.h
└── lib
├── libmodsecurity.3.dylib
├── libmodsecurity.a
├── libmodsecurity.dylib -> libmodsecurity.3.dylib
├── libmodsecurity.la
└── pkgconfig
└── modsecurity.pc

编译Nginx、ModSecurity-nginx

ModSecutiry可以部署于多种不同的Web服务器,Nginx,Apache等等,为了能用于不同的服务器,ModSecurity提供了一系列的连接器,将其与对应的服务器连接起来。

ModSecurity-nginx是连接ModSecurity和Nginx的桥梁,为了使ModSecurity能部署于Nginx,需要将ModSecurity-nginx与Nginx一起编译,并连接上一步安装的ModSecurity链接库。

这里有一个小坑,我最初在本地是使用 Homebrew 安装的 Nginx,但是没有找到如何为其添加外部模块的方法,最后不得不手动编译Nginx

首先需要下载Nginx源码(https://github.com/nginx/nginx)与ModSecurity-nginx源码(https://github.com/SpiderLabs/ModSecurity-nginx),并在编译Nginx时将ModSecurity-nginx指定为其外部模块:

1
2
./configure --add-module=/path/to/ModSecurity-nginx
sudo make install

在编译Nginx时遇到问题,总是提示无法找到ModSecurity链接库,按照网上的做法设置环境变量:

1
2
3
4
# modsecurity
export MODSECURITY_LIB="/usr/local/modsecurity/lib"
export MODSECURITY_INC="/usr/local/modsecurity/include"
export LD_LIBRARY_PATH="/usr/local/modsecurity/lib"

后仍然报错。通过检查编译脚本发现,脚本会在检查完Nginx本身的条件之后检查第三方模块的条件,而报错的原因在于ngx_found这个变量没有被正确赋值:

1
2
3
4
5
6
7
8
9
10
   # modsecurity-nginx/config

. auto/feature

if [ $ngx_found = no ]; then
cat << END
$0: error: ngx_http_modsecurity_module requires the ModSecurity library and MODSECURITY_LIB is defined as "$MODSECURITY_LIB" and MODSECURITY_INC (path for modsecurity.h) "$MODSECURITY_INC", but we cannot find ModSecurity there.
END
exit 1
fi

这个变量定义自nginx-1.24.0/auto/feature,进一步检查脚本发现,测试的原理是拼贴出一段C代码,并按照读取到的环境变量进行编译,如果能够编译成功则说明库正确安装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# nginx-1.24.0/auto/feature

cat << END > $NGX_AUTOTEST.c

#include <sys/types.h>
$NGX_INCLUDE_UNISTD_H
$ngx_feature_incs

int main(void) {
$ngx_feature_test;
return 0;
}

END

其中的变量$ngx_feature_incs就是引入头文件ngx_feature_incs="#include <modsecurity/modsecurity.h>"

$ngx_feature_test是简单的一句输出ngx_feature_test='printf("hello");'

问题出现在上面的C代码用到了printf但是并没有引入头文件stdio.h,导致无法通过编译。但是脚本认为没有通过编译就是因为没有正确安装ModSecurity。只要添加一句#include<stdio.h>,即可成功完成编译。

启动ModSecurity并配置WAF过滤规则

在Nginx的配置文件中开启modsecurty并指定其采用的规则集

1
2
3
4
5
6
7
8
9
# nginx.conf

modsecurity on; # 开启ModSecurity
location / {
modsecurity_rules_file modsec_includes.conf; #指定规则集

root html;
index index.html index.htm;
}

规则集简单起见直接使用owasp-modsecurity-crs,在下载规则集后,将其导入到我们的自定义规则集modsec_includes.conf当中:

1
2
3
include modsecurity.conf
include owasp-modsecurity-crs/crs-setup.conf
include owasp-modsecurity-crs/rules/*.conf

配置好规则之后重启Nginx,用curl发送请求,即可在log中看到。

为了测试modsecurity的拦截效果,试着自定义规则集中添加一条规则:

1
SecRule ARGS:testparam "@contains test" "id:1234,deny,status:403"

这条规则说明当请求中包含参数 testparam 同时其值为 test 时,拦截请求并返回 403。

本文采用CC-BY-SA-3.0协议,转载请注明出处
作者: TieStone