
一台对公网开放的 Web 服务器,只要运行几天,日志里就会出现成批的试探:撞库脚本反复 POST 登录接口,扫描器在 URL 参数里塞进 ' OR 1=1 之类的 SQL 注入载荷,还有尝试向评论区写入 <script> 标签的跨站脚本攻击。这些攻击有一个共同点——全部走 HTTP 层,系统防火墙只能按 IP 和端口放行,根本看不到请求内容。这篇文章将手把手教你如何完成 ModSecurity WAF 配置与误报调优,以 WordPress 站点为例走完从安装、接入规则集到安全切换拦截模式的全过程,帮助你在 HTTP 层把这些常见攻击直接拦下,而不是等网站被挂马或拖库之后再补救。
ModSecurity 是什么:给 Web 服务加一层内容防火墙
WAF(Web 应用防火墙,Web Application Firewall)工作在 HTTP 请求到达网站程序之前的位置,负责检查请求的 URL、参数、请求头和请求体,一旦匹配到攻击特征就记录、拦截或改写请求。ModSecurity 是目前使用最广泛的开源 WAF 引擎,Apache 和 nginx 都有对应的模块,它本身不内置具体的攻击特征,而是提供一个规则引擎,真正的检测能力来自 OWASP CRS(OWASP 官方维护的核心规则集),其中包含数百条针对 SQL 注入、XSS、路径穿越、文件包含等攻击的规则。
与系统层防护的分工可以这样理解:iptables 和云厂商安全组决定”谁能连到 80 端口”,ModSecurity 决定”这个请求的内容能不能到达 PHP”。两者不是替代关系,而是叠加关系。如果你的服务器上还跑了 SSH 服务,配合 fail2ban 做登录防爆破,就能把网络层、系统层和应用层各守一道门。

安装:以 nginx + Ubuntu 22.04 为例
ModSecurity 有 v2 和 v3(libmodsecurity)两个大版本。Apache 上常用 v2 模块,nginx 则需要通过 connector 调用 libmodsecurity。这里以最常见的 nginx 场景为例,Ubuntu 22.04 直接从官方仓库安装:
apt update && apt install -y libmodsecurity3 modsecurity nginx mkdir -p /etc/nginx/modsec cp /etc/modsecurity/modsecurity.conf-recommended /etc/nginx/modsec/modsecurity.conf
安装完成后,在 nginx.conf 的 http 块或对应 server 块里加载模块和主配置:
load_module modules/ngx_http_modsecurity_module.so;
server {
...
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/modsecurity.conf;
}
这一步先不要急着 reload。打开 /etc/nginx/modsec/modsecurity.conf,找到 SecRuleEngine 一行,默认值是 DetectionOnly——只记录不拦截,这是刻意安排的观察期,下一节会解释为什么必须保留它。用 nginx -t 验证配置语法无误后再 systemctl reload nginx,此时 ModSecurity 引擎已经挂载,但还没有任何检测规则,需要接入规则集。
接入 OWASP CRS:让引擎真正具备检测能力
从 CRS 3.x 开始,官方推荐通过 git 安装,方便后续按版本号升级:
git clone https://github.com/coreruleset/coreruleset /opt/coreruleset cd /opt/coreruleset cp crs-setup.conf.example crs-setup.conf
然后在 /etc/nginx/modsec/modsecurity.conf 末尾追加两行 include,注意顺序不能颠倒——先加载 CRS 的基础配置,再加载规则文件:
Include /opt/coreruleset/crs-setup.conf Include /opt/coreruleset/rules/*.conf
再次 nginx -t 通过后 reload,WAF 就进入了 DetectionOnly 观察模式。可以用一条最经典的测试载荷验证规则是否生效——向任意带参数的页面发起请求:
curl "https://example.com/?id=1' OR '1'='1" tail -f /var/log/nginx/error.log | grep ModSecurity
如果 error.log 里出现 id “942100”(SQL 注入检测规则)的命中记录,说明规则集已经在工作,只是还没有真正拦截。这条日志也是后面排除误报时的定位依据。
观察期与误报处理:不要跳过的一周
直接把 SecRuleEngine 改成 On 听起来很痛快,但 CRS 官方文档明确反对这样做:规则集面对的是千差万别的业务,评论区、富文本编辑器、文件上传接口都可能携带看起来像攻击的内容。跳过观察期贸然开拦截,最可能的结局是正常用户被 403,而且你甚至不知道丢了多少订单。
我们建议的做法是让 DetectionOnly 至少运行 5-7 天,覆盖一个完整的业务周期,然后统计日志:把 error.log 里所有 [id “xxxxxx”] 的规则号提取出来排序,出现频率最高的几条几乎一定是误报集中区。定位到具体规则后,不要修改 CRS 官方规则文件本身(升级时会被覆盖),而是在规则加载之后写自己的排除配置。例如某个后台富文本接口频繁误触 942100,可以按 URL 路径整体放宽:
SecRule REQUEST_URI "@beginsWith /wp-admin/post.php" \
"id:1000001,phase:1,pass,nolog,ctl:ruleRemoveById=942100"
排除项要有明确的 id(自定义规则约定使用 1 开头的七位数字)和尽可能窄的匹配范围——只针对特定路径、特定参数,而不是全局关掉某条规则。每一条排除都应该能回答”为什么这条规则在这个接口上是误报”。

切换拦截模式并验证防护效果
误报清理完成后,把 modsecurity.conf 里的 SecRuleEngine DetectionOnly 改为 On,reload 之前先想好回退路径:如果切换后出现大面积异常,把这一行改回来 reload 即可恢复,观察期积累的日志不会丢失。
切换后用三条典型载荷做一次验证,确认拦截行为符合预期:
curl -s -o /dev/null -w "%{http_code}\n" "https://example.com/?id=1' OR '1'='1"
curl -s -o /dev/null -w "%{http_code}\n" "https://example.com/?q="
curl -s -o /dev/null -w "%{http_code}\n" "https://example.com/etc/passwd"
三条请求分别对应 SQL 注入、XSS 和路径穿越,正常情况下都会返回 403,同时 error.log 记录命中的规则号。再访问几个正常页面确认 200 没有被误伤,WAF 就算正式上线了。上线之后还有两件事值得马上做:一是配置日志轮转和定期复查,每周看一眼误报趋势;二是把 WAF 拦截日志与 服务器加密备份一起纳入例行巡检——拦截是止损,备份是兜底,被入侵后能否快速恢复取决于后者。

WAF 不是万能的:认清边界
ModSecurity 能拦下绝大多数已知的模式化攻击,但有几类风险它管不了。零日漏洞的攻击载荷没有已知特征,规则集更新永远晚于漏洞披露;业务逻辑漏洞(例如越权查看他人订单)在请求内容上完全正常,WAF 无法判断意图;如果网站程序本身长期不更新,只靠 WAF 被动挨打迟早会被绕过。所以完整的防护思路应该是三层叠加:系统和应用及时打补丁是根本,文件权限与后台加固压缩攻击面,WAF 负责在应用层补上最后一道模式化攻击的拦截。另外,自建 WAF 意味着你要自己维护规则升级、误报排查和日志巡检,这对缺少运维人手的小团队是不小的负担。
自建与托管:怎么选
自建 ModSecurity 的优势是零授权成本、规则完全可控、数据不出服务器;代价是初始配置约需半天,观察期与误报调优需要持续投入,且每台服务器都要单独维护。托管型方案——例如主机商在网关层提供的 WAF,由专业团队维护规则,对 WordPress 通用攻击开箱即用,代价是规则透明度低、深度定制受限。我们在实际运维中的经验是:有专职运维、业务接口复杂(富文本、开放 API 多)的团队适合自建;以 WordPress 等标准建站程序为主、人力有限的站点,选择 Hostease 带基础防护的 VPS(虚拟专用服务器)加托管 WAF 更省心,两者也可以并用——托管层拦大流量通用攻击,自建层做细粒度业务规则。
总结
回到开头的问题:HTTP 层的攻击必须回到 HTTP 层解决。ModSecurity 加 OWASP CRS 提供了一条经过大规模验证的路径——安装引擎、接入规则集、用 DetectionOnly 度过一个完整业务周期的观察期、按规则号精准排除误报、切换 On 并用三条载荷验证拦截。整个过程的关键纪律有两个:不要跳过观察期,不要全局关闭规则。如果你正准备动手,建议先在一台测试环境完整走一遍流程,再应用到生产站点;如果团队没有余力维护规则集,可以考虑把应用层防护交给托管方案,把精力放回业务本身。