Web应用每天都在承受各种自动化扫描、SQL注入尝试和CC攻击,仅靠网络层的防火墙远远不够。ModSecurity是一款开源的Web应用防火墙引擎,它可以嵌入Nginx,对每一个HTTP请求进行深度检测和拦截。本文将完整演示如何在Nginx上集成ModSecurity,并接入OWASP CRS规则集,搭建一套真正可用的WAF防护体系。

一、为什么选择Nginx加ModSecurity组合
ModSecurity最初是为Apache设计的,但自从3.x版本重构后,它以独立动态库(libmodsecurity)的形式存在,通过连接件(connector)与Nginx、Apache等Web服务器对接。这种架构带来几个明显优势:引擎与连接件解耦,升级规则引擎不需要重新编译整套逻辑;Nginx本身的事件驱动模型让它能以极低的资源消耗处理海量并发连接,把WAF串在Nginx层,可以在流量到达后端应用之前就完成过滤。
与购买商业WAF相比,这套方案完全开源,规则可自由定制,尤其适合自建服务器、私有化部署的场景。当然也要客观认识它的局限:自建WAF需要投入精力维护规则、处理误报,如果团队没有人负责安全运营,防护效果会打折扣。
二、安装libmodsecurity与Nginx连接件
以CentOS或RHEL系环境为例,先安装编译依赖,然后从源码构建libmodsecurity。Ubuntu系统可以用apt直接安装libmodsecurity3和libmodsecurity-dev,省去编译步骤,生产环境推荐直接用发行版包以降低维护成本。
# 安装依赖(CentOS)
yum install -y gcc make automake autoconf libtool pcre-devel \
libxml2-devel curl-devel yajl-devel GeoIP-devel
# 编译安装libmodsecurity
git clone --depth 1 -b v3.0.12 https://github.com/owasp-modsecurity/ModSecurity.git
cd ModSecurity
git submodule init
git submodule update
./build.sh && ./configure
make && make install接下来编译ModSecurity-Nginx连接件。注意连接件不需要单独安装,只需要编译出 ngx_http_modsecurity_module.so 动态模块,再让Nginx加载它。如果你是自行编译Nginx,也可以直接在configure阶段通过--add-module静态编译进去。
# 编译Nginx动态模块(版本必须与线上Nginx一致) git clone --depth 1 https://github.com/owasp-modsecurity/ModSecurity-nginx.git cd /usr/local/src/nginx-1.24.0 ./configure --with-compat --add-dynamic-module=/usr/local/src/ModSecurity-nginx make modules cp objs/ngx_http_modsecurity_module.so /etc/nginx/modules/
编译完成后,在nginx.conf顶部加入load_module指令加载模块,然后用nginx -t验证配置是否合法。这里最常踩的坑是动态模块与Nginx版本不匹配,编译时的configure参数要和线上nginx -V输出的保持一致,否则加载时会直接报"module is not binary compatible"错误。
三、核心配置与OWASP CRS规则集接入
模块加载成功后,需要准备ModSecurity引擎配置文件。从源码目录复制modsecurity.conf-recommended,改名为modsecurity.conf,再复制unicode.mapping到同一目录。这个文件里有几个关键指令需要理解清楚。
# /etc/nginx/modsecurity/modsecurity.conf 核心配置 SecRuleEngine DetectionOnly SecRequestBodyAccess On SecRequestBodyLimit 13107200 SecRequestBodyLimitAction Reject SecAuditEngine RelevantOnly SecAuditLog /var/log/modsec_audit.log SecAuditLogFormat JSON SecTmpDir /var/lib/modsecurity SecDataDir /var/lib/modsecurity
其中SecRuleEngine有三个可选值:Off关闭引擎、DetectionOnly只记录不拦截、On正式拦截。强烈建议新部署的站点先跑一段时间的DetectionOnly,观察审计日志确认没有误报后再切到On,否则一条规则误判就可能导致整站正常请求被拒绝。SecRequestBodyLimit限制请求体大小,防止攻击者用超大包绕过检测。
规则集方面,OWASP Core Rule Set(CRS)是目前最主流的社区规则库,覆盖SQL注入、XSS、路径穿越、扫描器识别等常见攻击向量。下载CRS后,将crs-setup.conf.example复制为crs-setup.conf,并通过Include语句把配置串联起来。
# 站点配置中的WAF部分
server {
listen 80;
server_name example.ipipp.com;
modsecurity on;
modsecurity_rules_file /etc/nginx/modsecurity/main.conf;
}
# main.conf 内容
Include /etc/nginx/modsecurity/modsecurity.conf
Include /etc/nginx/modsecurity/crs-setup.conf
Include /etc/nginx/modsecurity/rules/*.conf修改规则后执行nginx -s reload即可生效,无需重启服务。验证方法也很简单:访问/?id=1' or '1'='1这类注入payload,如果处于On模式应返回403,DetectionOnly模式则可在audit日志中看到命中规则的记录。
四、误报处理、日志排查与性能优化
CRS默认规则对表单富文本编辑器、REST API的JSON字段经常误报,典型现象是用户提交文章内容时被403拦截。排查时先看/var/log/modsec_audit.log,JSON格式日志中会标注命中的规则ID。处理误报不建议直接注释规则,正确做法是在站点配置中用SecRuleRemoveById按ID关闭,或用SecRuleUpdateTargetById把特定参数排除在检测范围外,粒度越细越好。
# 仅对特定接口关闭某条规则
location /api/article/save {
modsecurity on;
modsecurity_rules '
SecRuleRemoveById 942100
SecRuleUpdateTargetById 941100 "!ARGS:content"
';
proxy_pass http://backend;
}性能层面,ModSecurity会带来5%到15%左右的吞吐损耗,规则数量越多开销越大。可以采取几个优化手段:在CRS的crs-setup.conf中启用静态IP直连模式(假设Nginx就是边界,没有CDN),关闭不必要的检测引擎,比如站点不含Java就关掉对java相关的规则包;将paranoia级别维持在PL1,除非有更高的安全要求;同时确保SecTmpDir放在内存盘或SSD上,减少请求体落盘的IO开销。
最后别忘了日志轮转和监控。audit日志增长非常快,建议配合logrotate按天切割,并部署简单的告警脚本统计拦截趋势。一旦规则误报率稳定、拦截记录符合预期,就可以把SecRuleEngine切换为On,让这套WAF从观察者变成真正的拦截者。安全防护是持续迭代的过程,定期升级CRS规则版本、关注新出现的绕过手法,才能让防线长期有效。
NginxModSecurityWAF配置修改时间:2026-08-31 13:19:03