第十章 —— WordPress安全实战指南 · Web Application Firewall部署

📌 第二部分:WAF部署

🤔 什么是 WAF?

WAF,全称 Web Application Firewall(网页应用防火墙),是一种专门保护网站应用层安全的防御系统。

与传统防火墙主要针对 IP、端口不同,WAF 直接分析 HTTP 请求内容,能识别并拦截:

  • SQL 注入
  • XSS(跨站脚本攻击)
  • 文件上传漏洞
  • 目录遍历
  • WordPress 特有的攻击路径扫描(如 xmlrpc、wp-login、wp-json 枚举)

它像一个「守门员」,替你挡在服务器之前,拦掉大部分恶意请求。


📌 为什么 WordPress 更需要 WAF?

WordPress 是世界上使用最广泛的 CMS 系统,这也意味着它是攻击者重点盯上的目标。

即便你已经做好了 Nginx 安全头、权限配置、禁用路径访问等防护措施,仍然可能因为某个插件漏洞或未知请求被突破。WAF 正是你最后一道主动防御的防火墙。


📌 WAF 的优势是什么?
功能描述
精准拦截能基于规则分析请求内容,而非仅靠 IP 或端口判断
实时防护无需重启服务即可立即生效,规则可热更新
攻击可视化可与日志系统配合监控攻击路径、频率、来源
可自定义规则可根据你的网站特点定制防护策略,如限制访问 /wp-login.php

🧰 你的选择:
  • ModSecurity + Core Rule Set(CRS):功能强大,开源,适用于 Nginx
  • 商业 WAF(如 Cloudflare WAF):可视化面板+智能学习
  • 自定义规则拦截(通过 Nginx Lua / Fail2ban + 日志组合)

🛡️ WAF 配置大致步骤:

  • 🔧 部署防护模块(如 Nginx + ModSecurity、OpenResty 等)
  • 📝 启用并加载基础规则集(如 OWASP CRS)
  • ✅ 自定义放行规则,适配业务需求
  • 🔄 定期更新规则集,应对新型攻击

📝 小结:

WAF 并不是万能的,但它是网站安全的关键一环。对 WordPress 这样公开暴露、插件繁多的系统而言,它是“能挡下 90% 自动化攻击”的护城河。

编译并安装ModSecurity

用git命令将完整的ModSecurity源码从github上拉取下来

$ cd /usr/local/src
$ sudo git clone --recursive https://github.com/owasp-modsecurity/ModSecurity ModSecurity

安装依赖包,这些包在后续的编译过程中会用到。

$ sudo dnf groupinstall 'Development Tools'
$ sudo dnf install pcre-devel ssdeep-devel libmaxminddb-devel yajl-devel lmdb-devel zlib-devel libcurl-devel libxml2-devel lua-devel jansson-devel openssl-devel pcre2-devel

这些开发包(*-devel)是在从源代码编译 ModSecurity(尤其是 v3) 时常见的依赖项。它们提供了对应库的头文件和开发接口,ModSecurity 及其依赖组件会根据功能模块需要引用这些库。下面是它们的作用一览:

📦 编译 ModSecurity 所需开发库解释:
开发包作用说明
pcre-devel提供 PCRE(Perl兼容正则表达式) 支持,ModSecurity 使用它进行规则匹配(ModSecurity v2 用得更多)。
pcre2-devel提供 PCRE2 支持,是 PCRE 的新版实现,ModSecurity v3 支持这类更现代的正则引擎。
ssdeep-devel用于 模糊哈希(fuzzy hashing) 检测相似内容,可用于检测相似攻击载荷。
libmaxminddb-devel支持 GeoIP2 功能,通过 IP 查询用户地理位置,有助于规则中基于地域做决策。
yajl-devel提供 JSON 解析能力(Yet Another JSON Library),ModSecurity 需要解析 JSON 请求体时依赖它。
lmdb-devel提供 高性能嵌入式数据库 支持,可用于存储缓存、规则状态、IP黑白名单等。
zlib-devel支持 gzip/zlib 压缩格式的内容处理,比如解压请求体、分析被压缩的数据。
libcurl-devel支持 ModSecurity 与外部服务进行 HTTP 请求(如远程验证或联动分析)。
libxml2-devel提供 XML 解析功能,当规则中需要解析或验证 XML 请求体时使用。
lua-develModSecurity 支持 Lua 扩展脚本,允许用户用 Lua 编写高级自定义规则逻辑。
jansson-devel又一个 JSON 库,用于解析 JSON 请求体或结构化数据,某些版本的 ModSecurity 会优先支持它。
openssl-devel提供 TLS/加密算法支持,ModSecurity 与加密相关的分析(如 HMAC 校验)可能用到它。
📌 小结:
  • ✅ 大部分是为了让 ModSecurity 能分析复杂请求体结构(如 JSON/XML)、执行正则规则、进行地理/IP匹配或扩展功能(如 Lua 脚本)。
  • 🛠️ 缺失这些依赖会导致部分功能无法启用或编译失败。
  • 🧩 实际依赖可根据你启用的模块精简,默认全部安装。

$ cd ModSecurity
$ sudo ./build.sh

./build.sh 是一个构建脚本,常见于手动编译项目时。它的作用是自动化执行一系列构建准备步骤,通常比 ./configure 更“高级”,因为它可能还会调用 git, cmake, make, autoconf, libtool 等工具组合使用。

🔍 通常 ./build.sh 会做的事情有:
  1. 初始化构建环境
    • 运行 git submodule update --init --recursive
    • 确保依赖的源码库或子模块同步到位(例如 ModSecurity v3 用到 libinjection
  2. 生成 configure 文件
    • 如果项目是 autotools 构建体系,它可能会运行: autoreconf -fiv
    • 这一步会创建 configure 脚本,让你后续可以手动执行 ./configure
  3. 准备构建系统
    • 设置 libtool, aclocal, automake 等构建工具用的元文件
  4. 输出提示
    • 有些 build.sh 会检查你是否已经安装了必要的 *-devel 包,并输出警告信息或退出

$ sudo ./configure --with-lmdb

默认情况下,构建过程中会将所有的功能都启用,可是不知道什么原因lmdb需要添加–with-lmdb选项来手动启用它。如果不加这个选项,输出的结果会显示lmdb disabled.
如果添加了–with-lmdb选项仍然显示disabled,那么请执行以下命令后重新执行./configure --with-lmdb进行新一轮的构建。

$ sudo echo "/usr/lib64" | sudo tee /etc/ld.so.conf.d/lmdb.conf #告诉系统 lmdb 的库文件在 /usr/lib64 目录
$ sudo ldconfig -p |grep lmdb #查看系统中是否已经识别并缓存了 lmdb 的库文件路径。
$ export CFLAGS="-I/usr/include" #设置 C 编译器的头文件搜索路径。
$ export LDFLAGS="-L/usr/lib64" #设置链接器的库文件搜索路径。

./configure 是大多数源代码包中用于准备构建的脚本,它是由 autoconf 工具生成的,主要作用是:

🧠 检测你的系统环境,并为 make 编译生成正确的 Makefile 等构建文件。

它主要做这些事:

1️⃣ 检查编译环境是否满足
  • 检查编译器(如 gccclang)是否存在
  • 检查必要工具是否安装(如 makearranlib
2️⃣ 检查头文件和库
  • 会尝试编译一些临时程序,测试是否有对应的头文件和函数:
    • #include <stdio.h> 是否能找到?
    • malloc() 是否能调用?
    • 是否链接得到 libpcre, liblmdb, libcurl 等库?
  • 会生成 config.logconfig.status 用于记录这些测试
3️⃣ 生成配置宏头文件(如 config.h
  • 里面包含大量宏定义,源码中可以通过条件编译选择不同代码路径。
4️⃣ 生成 Makefile
  • 读取 Makefile.in 模板,加上系统测试结果,生成最终可用的 Makefile
  • 这个文件定义了后续 make 的行为(编译哪些 .c、链接成哪些目标文件)
5️⃣ 根据参数自定义构建

你可以通过传参来自定义配置行为,例如:

示例作用
--prefix=/usr/local/modsecurity安装路径
--with-lmdb启用某功能
--with-pcre=/usr/local/pcre指定依赖库路径
--disable-ssdeep关闭某功能
🧾 执行后生成的文件
文件/目录用途
Makefile编译说明
config.h系统特性宏定义
config.status当前配置状态
config.log错误排查日志
✅ 总结一句话:

./configure 就是让源代码“适应”你当前系统的工具,它为后续 make 准备好所有路径、特性、库依赖,并生成定制化的编译配置。


$ sudo make
$ sudo make install

makemake install 是基于 Makefile 的标准构建流程中的两个关键命令,它们是你完成源码编译和安装的最终步骤。

🧱 一、make 的作用

💡 根据 Makefile 的规则编译源码,生成可执行文件、库或目标文件。

🔧 详细来说:
  • 调用系统编译器(如 gccclang)编译 .c 文件为 .o 文件
  • 将多个 .o 目标文件链接成:
    • 可执行程序(如 modsecurity
    • 动态库(如 libmodsecurity.so
    • 静态库(如 libmodsecurity.a
✅ 最终结果:
  • 文件仍然保留在源码目录下(比如 src/build/ 目录)
  • 并不会被安装到系统路径
📦 二、make install 的作用

💡 将 make 编译生成的内容复制到系统指定目录,例如 /usr/local/bin/usr/lib 等。

⚙️ 它通常会做:
  • 把可执行程序安装到系统目录(如 /usr/local/bin/modsecurity
  • 把头文件安装到 /usr/local/include
  • 把库文件 .so.a 安装到 /usr/local/lib
  • 把文档、配置文件安装到 /usr/local/share, /etc

ModSecurity安装结束,最终的可执行文件和模块位于/usr/local/modsecurity

编译并安装modsecurity-nginx

modsecuritymodsecurity-nginx 是两个紧密关联但职责不同的组件,它们共同实现了 WAF(Web 应用防火墙)功能在 Nginx 上的运行。

modsecurity 是核心引擎,modsecurity-nginx 是连接 Nginx 和这个引擎的“适配器”模块。


🔍 两者的关系
项目说明
modsecurity🔧 核心库(libmodsecurity):实现了 WAF 的规则引擎、日志系统、攻击检测等。它是通用的,可以给 Apache、Nginx、甚至嵌入式系统使用。
modsecurity-nginx🔗 适配模块,让 Nginx 能调用 modsecurity 引擎处理 HTTP 请求,是一个中间层。

🔧 安装过程

为了在 Nginx 中使用 WAF,通常流程是:

  1. ✅ 先编译和安装 modsecurity(已完成)
  2. ✅ 然后编译 modsecurity-nginx 模块,并在构建 Nginx 时通过 --add-module 加进去
  3. ✅ 在 Nginx 配置文件中启用ModSecurity

📌 如果你只是安装了 modsecurity 而没有 modsecurity-nginx,Nginx 是无法使用 WAF 功能的。反之也一样:modsecurity-nginx 只是接口,不能单独工作。

在编译modsecurity-nginx之前,需要下载nginx的源码包,用于后续的动态模块编译

$ nginx -v #假设版本号是1.20.1
$ cd /usr/local/src
$ sudo wget http://nginx.org/download/nginx-1.20.1.tar.gz
$ sudo tar -xzf nginx-1.20.1.tar.gz
$ cd nginx-1.20.1/

下载modsecurity-nginx源码,动态编译nginx模块

$ sudo git clone https://github.com/SpiderLabs/ModSecurity-nginx
$ sudo ./configure --with-compat --add-dynamic-module=./ModSecurity-nginx #--with-compat的目的是确保编译的模块与原有的安装兼容,例如原来是通过dnf等包管理器安装的情况
$ sudo make modules #这里无需编译整个nginx,只要编译模块即可
$ sudo cp objs/ngx_http_modsecurity_module.so /usr/share/nginx/modules/

配置规则

下载规则库

$ cd /usr/local/src
$ sudo git clone https://github.com/coreruleset/coreruleset.git #下载规则库
$ cd coreruleset
$ sudo git describe --tags #查看规则库的版本

复制相关文件到nginx目录

$ cd .. #退回到/usr/local/src目录
$ sudo mkdir /etc/nginx/modsecurity
$ sudo cp ModSecurity/modsecurity.conf-recommended /etc/nginx/modsecurity/modsecurity.conf #这个是ModSecurity的核心配置文件
$ sudo cp ModSecurity/unicode.mapping /etc/nginx/modsecurity/unicode.mapping #这个是Unicode映射表
$ sudo cp -r /usr/local/src/coreruleset/rules /etc/nginx/modsecurity/ #复制整个rules目录
$ sudo cp /usr/local/src/coreruleset/crs-setup.conf.example /etc/nginx/modsecurity/crs-setup.conf #规则集初始化配置模板
$ cd /etc/nginx/modsecurity/rules/
$ sudo mv REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
$ sudo mv RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
📄 常见 ModSecurity/CRS 配置文件作用说明
文件名作用说明
modsecurity.conf-recommendedModSecurity 官方提供的推荐主配置模板,包含引擎启用、日志格式、检测模式(SecRuleEngine)、请求体限制等核心设置。需重命名为 modsecurity.conf 才生效。
unicode.mappingUnicode 映射表,用于将 HTTP 请求中的编码字符(如 %u002f)还原为实际字符,防止编码绕过攻击。通过 SecUnicodeMapFile 引用,属于 ModSecurity 核心配置之一。
crs-setup.conf.exampleOWASP CRS 提供的初始化配置模板,用于设置全局规则行为,如检测敏感度(paranoia_level)、阈值(anomaly_score_threshold)、允许方法等。需复制为 crs-setup.conf 才会生效。
REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example在加载核心规则(900 之后)之前运行的排除规则模板,用于根据 URI、参数、IP 等自定义白名单规则,避免误报。建议在生产环境启用并定制。
RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example在所有规则执行完毕之后运行的排除规则模板,常用于放行已知业务中的合法响应误报,也可用于记录特定响应行为。同样需要复制并启用。

启用modsecurity.conf并添加规则库

$ sudo vim /etc/nginx/modsecurity/modsecurity.conf

SecRuleEngine On #启用ModSecurity引擎

#添加规则库
Include /etc/nginx/modsecurity/crs-setup.conf
Include /etc/nginx/modsecurity/rules/*.conf

在Nginx中启用ModSecurity

$ sudo vim /etc/nginx/nginx.conf

load_module /usr/share/nginx/modules/ngx_http_modsecurity_module.so; #添加在顶部,作为全局配置

#以下部分加在http block内,server块之外,作用于整个http block
modsecurity on;
modsecurity_rules_file /etc/nginx/modsecurity/modsecurity.conf;

重启Nginx

$ sudo systemctl restart nginx.service

检查/var/log/nginx/error.log日志会发现一共加载多少条规则

[notice] 197072#197072: ModSecurity-nginx v1.0.3 (rules loaded inline/local/remote: 0/816/0)

被拒绝的例子:

[error] 192055#192055: *51687 [client 51.89.153.66] ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Ge' with parameter `5' against variable `TX:BLOCKING_INBOUND_ANOMALY_SCORE' (Value: `5' ) [file "/etc/nginx/modsecurity/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "222"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 5)"] [data ""] [severity "0"] [ver "OWASP_CRS/4.13.0-dev"] [maturity "0"] [accuracy "0"] [tag "anomaly-evaluation"] [tag "OWASP_CRS"] [hostname "1.2.3.4"] [uri "/xmlrpc.php"] [unique_id "175006734466.569961"] [ref ""], client: 51.89.153.66, server: www.example.com, request: "POST /xmlrpc.php HTTP/1.1", host: "example.com"

配置SELinux策略

ModSecurity 可能会使用 LuaJIT 来提高性能,因此 Nginx 在启用了 ModSecurity(特别是 OWASP Core Rule Set)时,可能会触发 execmem 访问请求。这是因为 LuaJIT 运行时 需要分配可执行和可写的内存区域,而 SELinux 默认禁止这种行为。你可能会在audit日志中发现如下的错误信息:

$ sudo sealert -a /var/log/audit/audit.log

#输出如下(省略了部分信息)
SELinux is preventing /usr/sbin/nginx from using the execmem access on a process...

这句话的意思是 SELinux 阻止了 /usr/sbin/nginx 进程在某个进程上执行 execmem 操作。

什么是 execmem?

execmem(Executable Memory)权限允许进程分配一个 既可写(writable)又可执行(executable) 的内存区域。这种操作可能被用来:

  • 运行 JIT(Just-In-Time)编译器(例如 JavaScript 引擎、LuaJIT 等)
  • 加载动态代码(如某些模块需要修改自身代码)
  • 攻击者利用漏洞执行代码注入(这就是 SELinux 阻止它的原因)
SELinux 提供的解决方案

方案 1:启用 SELinux 允许的 httpd_execmem boolean

*****  Plugin allow_execmem (53.1 confidence) suggests   *********************
If you know why nginx needs to map a memory region that is both executable and writable and understand that this is a potential security problem.
Then you can allow the mapping by switching one of the following booleans: httpd_execmem
Do
setsebool -P httpd_execmem 1

解释:

  • httpd_execmem SELinux 布尔值 决定是否允许 Nginx(httpd_t 类型) 进程执行 execmem 操作。
  • setsebool -P httpd_execmem 1 这个命令可以 永久允许 httpd_t 进程执行 execmem

方案 2:创建一个 SELinux 本地策略模块

*****  Plugin catchall (5.76 confidence) suggests   **************************
If you believe that nginx should be allowed execmem access on processes labeled httpd_t by default.
Then you should report this as a bug.
You can generate a local policy module to allow this access.
Do
ausearch -c 'nginx' --raw | audit2allow -M my-nginx
semodule -X 300 -i my-nginx.pp

解释:

  • ausearch -c 'nginx' --raw | audit2allow -M my-nginx
    • 这条命令会从 audit 日志 生成一个允许 nginx 执行 execmem 的 SELinux 策略模块。
  • semodule -X 300 -i my-nginx.pp
    • 这个命令会 安装并加载 这个本地 SELinux 策略模块。
推荐方案

如果你的服务器 专门用于 Nginx + ModSecurity,并且你信任 Nginx 的代码,使用 方法 1(全局启用 httpd_execmem) 是最简单的解决方案。
如果你的服务器上运行多个 Web 服务,并且你希望 最小化安全风险,建议使用 方法 2(创建本地 SELinux 策略),只对 Nginx 赋权。


如果采用方案2,下面的命令用于列出模块、删除模块操作,用于验证规则是否生效。

$ sudo semodule -l
$ sudo semodule -lfull |grep my-nginx
$ sudo semodule -X 300 -r my-nginx 

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注