当你的 Nginx 服务器暴露在公网时,面临的威胁远比想象中复杂。从网络层的容量型 DDoS(如 UDP Flood、ICMP Flood),到更具迷惑性的应用层 CC 攻击(HTTP Flood),再到 OWASP Top 10 中的常客——SQL 注入、XSS、路径遍历,攻击面覆盖了从网络到应用的全栈。如何利用 Nginx 构建一道有效的防线?本文将从实战出发,为你梳理从基础安全加固到集成 ModSecurity WAF 的全套配置方案。
一、防护体系概述
1.1 威胁背景与分层防御
CC攻击(Challenge Collapsar)是当前最难防御的应用层攻击之一。攻击者模拟正常的 HTTP 请求,专门针对那些计算成本高的接口(如搜索、登录、生成验证码)发起海量并发,目的就是耗尽后端资源。它与 SYN Flood 不同,其流量在协议层完全“合法”,传统防火墙难以识别,必须在应用层实施精准的速率控制。
至于 SQL 注入、XSS 的危害已无需赘述。而路径遍历(../../../etc/passwd)和敏感文件泄露(.git/config、.env)这类由配置疏忽导致的“低垂果实”,在实际渗透测试中命中率极高。
Nginx 作为市场占有率第一的 Web 服务器和反向代理,其自身的安全配置质量直接决定了服务的攻击面大小。其原生模块如 ngx_http_limit_req_module(限流)、ngx_http_limit_conn_module(限连接)、ngx_http_geo_module(地理位置)提供了基础的防护能力。结合功能强大的 ModSecurity 第三方 WAF,我们就能构建起一套分层、立体的应用层防护体系。
1.2 技术方案定位
需要明确的是,Nginx 原生安全能力与第三方 WAF 方案并非替代关系,而是互补和叠加。
- Nginx原生模块 主要处理流量控制层的问题:连接数限制、请求速率限制、IP黑白名单、HTTP方法过滤。这些操作在请求进入上游(upstream)之前完成,性能开销极低,适合应对大规模流量冲击,是抵御 DDoS 和 CC 攻击的第一道闸门。
- ModSecurity 3.0 则是真正的 WAF 引擎,工作在请求内容检测层:它会深度解析 HTTP 请求体,通过正则表达式等规则匹配来检测注入特征。OWASP Core Rule Set(CRS)提供了开箱即用的规则集,覆盖了 SQL 注入、XSS、远程/本地文件包含(RFI/LFI)、命令注入等主流攻击类型。
下表清晰展示了几种方案的定位差异:
| 方案 |
防护层次 |
性能影响 |
规则灵活性 |
适用场景 |
| Nginx原生限流 |
流量控制层 |
极低(<1ms) |
中等 |
大流量DDoS/CC防护 |
| ModSecurity+CRS |
内容检测层 |
中等(2-10ms) |
高 |
OWASP Top 10防护 |
| Nginx geo模块 |
IP访问控制层 |
极低 |
低 |
地域封锁、IP黑名单 |
| Lua/OpenResty |
动态逻辑层 |
低 |
极高 |
复杂业务规则 |
1.3 适用场景与前置准备
这套组合拳非常适合以下场景:
- 中小型WAF部署:对于流量峰值在10Gbps以下、并发连接数在10万以内的业务,Nginx + ModSecurity 完全有能力承载,无需采购昂贵的专用硬件WAF设备。
- API网关防护:在微服务架构中,Nginx 常作为 API 网关。在此层面叠加 WAF 规则,可以在流量进入内部微服务网络之前完成安全过滤,避免攻击流量穿透。
- 边缘安全节点:在 CDN 回源节点或边缘计算节点部署,可以在距离用户最近的位置完成流量清洗,有效降低源站压力。
开始前,请确保你的环境满足以下要求:
| 组件 |
版本要求 |
说明 |
| 操作系统 |
Ubuntu 22.04+ / CentOS Stream 8+ |
推荐 Ubuntu 22.04 LTS |
| Nginx |
1.26.x (mainline) / 1.24.x (stable) |
需编译 --with-http_realip_module 等模块 |
| ModSecurity |
3.0.12+ |
即 libmodsecurity3,需单独编译 Nginx 连接器 |
| OWASP CRS |
4.x |
独立于 ModSecurity 版本的规则集 |
| GeoIP2数据库 |
MaxMind GeoLite2 |
需注册免费账号获取 License Key |
| libmaxminddb |
1.7.x+ |
GeoIP2 C 库依赖 |
二、详细配置步骤
2.1 基础安全加固:缩小攻击面
在部署高级功能前,先做好基础加固,这能有效阻挡大量自动化扫描和低级别攻击。
2.1.1 隐藏版本信息
默认配置下,Nginx 会在响应头中暴露具体版本号(如 Server: nginx/1.26.0),这无异于告诉攻击者该针对哪个版本的漏洞进行利用。
# /etc/nginx/nginx.conf - http 块全局配置
http {
# 关闭版本号暴露
server_tokens off;
# 自定义 Server 头(需编译 headers-more-nginx-module 模块)
more_set_headers 'Server: webserver';
# 若无此模块,仅 server_tokens off 也已足够。切勿设置为其他真实服务器名(如 Apache),以免造成误导。
}
2.1.2 禁用不安全的 HTTP 方法
生产环境通常只需要 GET、POST、HEAD 三种方法。TRACE 方法存在跨站追踪(XST)风险,而 DELETE/PUT/PATCH 若后端鉴权不严,则直接暴露危险操作入口。
# /etc/nginx/conf.d/security.conf
server {
# 采用白名单方式限制 HTTP 方法,比黑名单更安全。
# 返回 444(Nginx 特有,直接关闭连接不发送响应)比 405 更节省资源。
if ($request_method !~ ^(GET|POST|HEAD)$) {
return 444;
}
# 如果是 API 服务器,可能需要额外允许 PUT、DELETE、PATCH
# location /api/ {
# if ($request_method !~ ^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)$) {
# return 444;
# }
# }
}
2.1.3 请求体限制与超时控制
这是防御慢速攻击(如 Slowloris、Slow POST)的关键。Slowloris 通过保持大量半开连接耗尽服务器的连接池,慢速 POST 则以极低速率发送请求体,长时间占用 worker 进程。
# /etc/nginx/nginx.conf
http {
# 限制请求体大小,防止大文件上传攻击耗尽资源
client_max_body_size 10m;
# 读取请求头超时,防 Slowloris
client_header_timeout 5s;
# 读取请求体超时,防 Slow POST
client_body_timeout 10s;
# 发送响应超时,防客户端故意不接收响应
send_timeout 10s;
# 限制单个 keepalive 连接的请求数,防止攻击者用少量连接发起大量请求
keepalive_requests 100;
keepalive_timeout 65s;
# 限制请求头缓冲区大小,超大请求头往往是攻击特征
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
}
2.1.4 防止目录遍历与敏感文件泄露
这是渗透测试中最常见且最容易修复的漏洞之一。
# /etc/nginx/conf.d/security.conf
server {
# 禁止访问所有隐藏文件(.git, .env, .htaccess 等)
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# 禁止访问备份文件、临时文件
location ~* \.(bak|sql|tar|gz|zip|log|conf|ini|old|orig|tmp)$ {
deny all;
access_log off;
log_not_found off;
}
# 禁止访问版本控制目录
location ~ ^/(\.git|\.svn|\.hg) {
deny all;
}
# 禁止目录列表,防止目录遍历
autoindex off;
# 添加关键安全响应头
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# CSP需根据业务具体配置,此处仅为示例
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'" always;
}
2.2 DDoS 与 CC 攻击防护配置
2.2.1 连接数限制(limit_conn 模块)
limit_conn 限制的是同一时刻的并发连接数,而非请求速率。正常浏览器并发通常在 2-6 个,单个 IP 超过 20 个就值得警惕。
# /etc/nginx/nginx.conf - http 块
http {
# 定义连接数共享内存区,10m 可存储约 16 万个 IP 的状态
limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;
limit_conn_status 503; # 超限返回状态码
limit_conn_log_level warn;
}
server {
# 每个 IP 全局最多 20 个并发连接
limit_conn conn_per_ip 20;
# 静态资源可适当放宽,因为浏览器会并发请求多个资源
location /static/ {
limit_conn conn_per_ip 50;
}
# 登录接口需严格限制,防止撞库攻击
location /api/login {
limit_conn conn_per_ip 5;
}
}
2.2.2 请求速率限制(limit_req 模块,漏桶算法)
limit_req 基于漏桶算法。请求以任意速率进入“桶”,但以固定速率(rate)流出处理。burst 定义桶的容量,nodelay 决定桶内积压的请求是立即处理还是排队等待。
# /etc/nginx/nginx.conf - http 块
http {
# 全局请求速率限制区
limit_req_zone $binary_remote_addr zone=req_per_ip:10m rate=10r/s;
# 登录接口严格限流区
limit_req_zone $binary_remote_addr zone=login_zone:10m rate=1r/s;
# API 接口限流区
limit_req_zone $binary_remote_addr zone=api_zone:10m rate=30r/s;
limit_req_status 429; # 超限返回 429 Too Many Requests,语义更准确
limit_req_log_level warn;
}
server {
# 全局限流:允许突发 20 个请求,超出部分立即拒绝(nodelay)
limit_req zone=req_per_ip burst=20 nodelay;
location /api/login {
# 登录接口:每秒1个请求,允许5个突发,超出立即拒绝
limit_req zone=login_zone burst=5 nodelay;
}
location /api/ {
limit_req zone=api_zone burst=50 nodelay;
}
}
2.2.3 基于地理位置的访问控制(GeoIP2 模块)
如果业务只面向特定地区用户,封锁其他地区的 IP 可以显著减少无谓的扫描流量。
# 安装依赖并下载 GeoIP2 数据库(需替换 YOUR_LICENSE_KEY)
apt install -y libmaxminddb0 libmaxminddb-dev mmdb-bin
wget -O /tmp/GeoLite2-Country.tar.gz \
"https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=YOUR_LICENSE_KEY&suffix=tar.gz"
tar -xzf /tmp/GeoLite2-Country.tar.gz -C /etc/nginx/geoip/
# /etc/nginx/conf.d/geoip.conf
# 需要编译 ngx_http_geoip2_module
geoip2 /etc/nginx/geoip/GeoLite2-Country.mmdb {
$geoip2_country_code country iso_code;
}
# 定义允许访问的国家/地区白名单
map $geoip2_country_code $allowed_country {
default 0; # 默认拒绝
CN 1; # 中国大陆
HK 1; # 香港
TW 1; # 台湾
MO 1; # 澳门
}
server {
# 非白名单国家返回 444,直接断开连接
if ($allowed_country = 0) {
return 444;
}
}
2.2.4 SYN Flood 防护(内核参数调优)
Nginx 层面的配置对 SYN Flood 无效,必须在操作系统内核层面进行加固。
# /etc/sysctl.d/99-nginx-security.conf
# SYN Flood 防护内核参数
# 启用 SYN Cookie,半连接队列满时使用 Cookie 验证
net.ipv4.tcp_syncookies = 1
# 增大半连接队列大小
net.ipv4.tcp_max_syn_backlog = 65536
# 减少 SYN 重传次数,加速回收半开连接
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_synack_retries = 2
# 允许复用 TIME_WAIT 连接,缓解端口耗尽
net.ipv4.tcp_tw_reuse = 1
# 应用配置
sysctl -p /etc/sysctl.d/99-nginx-security.conf
2.3 集成 ModSecurity WAF 进行深度内容检测
ModSecurity 3.0 是一个独立的 WAF 引擎库,Nginx 通过 ModSecurity-nginx 连接器模块与之交互,架构清晰,性能优于旧版。
2.3.1 编译安装 ModSecurity 3.0
# 安装编译依赖
sudo apt install -y build-essential git libpcre2-dev libxml2-dev \
libcurl4-openssl-dev libyajl-dev libgeoip-dev liblmdb-dev \
libfuzzy-dev pkg-config automake libtool
# 克隆并编译 ModSecurity
cd /usr/local/src
sudo git clone --depth 1 -b v3.0.13 https://github.com/owasp-modsecurity/ModSecurity.git
cd ModSecurity
sudo git submodule init
sudo git submodule update
sudo ./build.sh
sudo ./configure --with-pcre2 --with-lmdb
sudo make -j$(nproc)
sudo make install
# 验证:ls -la /usr/local/lib/libmodsecurity.so*
2.3.2 编译 Nginx 连接器模块
# 克隆连接器源码
cd /usr/local/src
sudo git clone --depth 1 https://github.com/owasp-modsecurity/ModSecurity-nginx.git
# 下载与当前 Nginx 版本一致的源码
NGINX_VER=$(nginx -v 2>&1 | grep -oP '\d+\.\d+\.\d+')
sudo wget http://nginx.org/download/nginx-${NGINX_VER}.tar.gz
sudo tar xzf nginx-${NGINX_VER}.tar.gz
cd nginx-${NGINX_VER}
# 基于当前 Nginx 参数编译动态模块
NGINX_ARGS=$(nginx -V 2>&1 | grep 'configure arguments' | sed 's/configure arguments: //')
eval sudo ./configure ${NGINX_ARGS} --add-dynamic-module=/usr/local/src/ModSecurity-nginx
sudo make modules
# 复制模块并加载
sudo cp objs/ngx_http_modsecurity_module.so /etc/nginx/modules/
# 然后在 nginx.conf 顶部添加: load_module modules/ngx_http_modsecurity_module.so;
2.3.3 配置 OWASP CRS 4.x 规则集
CRS 4.x 在误报率和检测精度上比 3.x 有显著提升。
# 下载 CRS
cd /etc/nginx
sudo git clone --depth 1 -b v4.7.0 \
https://github.com/coreruleset/coreruleset.git /etc/nginx/owasp-crs
sudo cp /etc/nginx/owasp-crs/crs-setup.conf.example /etc/nginx/owasp-crs/crs-setup.conf
ModSecurity 主配置文件:
# /etc/nginx/modsecurity/modsecurity.conf
# 规则引擎模式:DetectionOnly 只记录不拦截(调试用),On 为拦截模式
SecRuleEngine DetectionOnly
# 请求体检查
SecRequestBodyAccess On
SecRequestBodyLimit 13107200 # 请求体大小限制
SecRequestBodyNoFilesLimit 131072
# 响应体检查(对性能影响大,按需开启)
SecResponseBodyAccess Off
# 审计日志配置
SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
SecAuditLogType Serial
SecAuditLog /var/log/nginx/modsec_audit.log
# 临时文件目录
SecTmpDir /tmp/modsecurity/tmp
SecDataDir /tmp/modsecurity/data
# 加载 OWASP CRS
Include /etc/nginx/owasp-crs/crs-setup.conf
Include /etc/nginx/owasp-crs/rules/*.conf
在 Nginx 站点配置中启用:
# /etc/nginx/conf.d/waf.conf
server {
listen 443 ssl;
server_name example.com;
modsecurity on;
modsecurity_rules_file /etc/nginx/modsecurity/modsecurity.conf;
location / {
proxy_pass http://backend;
}
}
2.3.4 编写自定义规则
CRS 覆盖通用攻击,业务特有的安全需求需自定义规则。规则文件应在 CRS 之后加载。
# /etc/nginx/modsecurity/custom-rules.conf
# 规则1:拦截已知恶意扫描器的 User-Agent
SecRule REQUEST_HEADERS:User-Agent "@rx (?i)(sqlmap|nikto|nessus|dirbuster|gobuster)" \
"id:100001,\
phase:1,\
deny,\
status:403,\
log,\
msg:'恶意扫描器检测',\
tag:'custom/scanner-detection',\
severity:'CRITICAL'"
# 规则2:阻止双扩展名文件上传绕过
SecRule FILES_NAMES "@rx \.(php|jsp|asp|cgi)\." \
"id:100002,\
phase:2,\
deny,\
status:403,\
log,\
msg:'双扩展名文件上传拦截',\
tag:'custom/upload-protection'"
# 规则3:针对特定认证接口的增强速率限制(示例,与 limit_req 互补)
SecRule REQUEST_URI "@beginsWith /api/v1/auth" \
"id:100003,\
phase:1,\
pass,\
log,\
setvar:'ip.auth_counter=+1',\
expirevar:'ip.auth_counter=60'"
SecRule IP:AUTH_COUNTER "@gt 20" \
"id:100004,\
phase:1,\
deny,\
status:429,\
log,\
msg:'认证接口请求频率超限',\
tag:'custom/rate-limit'"
# 规则4:内部网络健康检查接口白名单(跳过WAF检测)
SecRule REMOTE_ADDR "@ipMatch 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16" \
"id:100010,\
phase:1,\
pass,\
nolog,\
ctl:ruleEngine=Off,\
chain"
SecRule REQUEST_URI "@beginsWith /health"
2.4 启动验证与监控
2.4.1 启动与基础验证
# 准备目录并检查配置
sudo mkdir -p /tmp/modsecurity/{tmp,data}
sudo chown www-data:www-data /tmp/modsecurity/{tmp,data}
sudo nginx -t
sudo nginx -s reload
# 确认模块加载:nginx -V 2>&1 | grep modsecurity
2.4.2 WAF 功能测试
# 测试 SQL 注入拦截
curl -i "https://example.com/?id=1' OR '1'='1"
# 测试 XSS 拦截
curl -i "https://example.com/?q=<script>alert(1)</script>"
# 测试路径遍历拦截
curl -i "https://example.com/../../etc/passwd"
# 测试自定义规则(恶意 User-Agent)
curl -i -A "sqlmap/1.7" "https://example.com/"
# 测试正常请求
curl -i "https://example.com/"
2.4.3 审计日志确认
确认 DetectionOnly 模式日志正常后,再将 SecRuleEngine 改为 On。
sudo tail -50 /var/log/nginx/modsec_audit.log
# 日志中应包含拦截记录,如 [id "942100"] [msg "SQL Injection Attack Detected via libinjection"]
三、运维实践与进阶
3.1 性能优化最佳实践
ModSecurity 的主要性能开销在于正则匹配。高并发下需优化:
3.2 自动化与监控
基于日志的自动封禁脚本:可以编写脚本分析 access.log,对短时间内触发大量 429/403 状态的 IP 自动加入 Nginx 黑名单,并通过 fail2ban 与 iptables 联动实现更长时间的封禁。详细的脚本示例和 fail2ban 配置因篇幅较长,其核心思想是:监控特定状态码(429限流、403WAF拦截)或匹配攻击特征,达到阈值后自动更新封禁列表并重载 Nginx。
集成监控系统:通过 nginx-prometheus-exporter 暴露 Nginx 指标(如 429/403 请求率、活跃连接数),在 Prometheus 中设置告警规则,当限流触发率或 WAF 拦截率异常升高时及时告警。同时,使用 Filebeat 采集 ModSecurity 审计日志至 ELK Stack,便于进行 安全事件分析 和追溯。
四、总结
Nginx 安全配置是一个典型的分层防御体系:内核参数加固网络层,原生模块高效过滤异常流量,ModSecurity WAF 深度检测内容,再辅以自动化工具进行主动响应。这套“廉价操作前置,昂贵操作后置”的组合拳,能以合理的成本为中小型 Web 服务提供坚实的安全保障。
配置完成后,切记要在测试环境充分验证,并建立持续的监控和备份机制。安全是一个持续的过程,规则需要随着业务和威胁的变化而不断调优。希望这份实战指南能帮助你更好地加固你的 Nginx 服务。如果在实践中遇到更多有趣的场景或解决方案,欢迎在 云栈社区 与大家分享讨论。