# NGINX 安全深度指南
## 目录
1. [Bot 检测与防护](#1-bot-检测与防护)
2. [WAF 配置深度指南(ModSecurity)](#2-waf-配置深度指南modsecurity)
3. [DDoS 防护策略](#3-ddos-防护策略)
4. [OWASP Top 10 防护](#4-owasp-top-10-防护)
5. [安全响应头完整配置](#5-安全响应头完整配置)
6. [CVE 历史漏洞与修复版本](#6-cve-历史漏洞与修复版本)
7. [安全审计日志配置](#7-安全审计日志配置)
8. [完整安全配置示例](#8-完整安全配置示例)
---
## 1. Bot 检测与防护
### 1.1 基于 User-Agent 的 Bot 识别
```nginx
map $http_user_agent $bot_type {
default "human";
# 善意爬虫(白名单)
~*googlebot "good_bot";
~*bingbot "good_bot";
~*duckduckbot "good_bot";
~*baiduspider "good_bot";
~*yandexbot "good_bot";
~*facebookexternalhit "good_bot";
~*twitterbot "good_bot";
~*linkedinbot "good_bot";
~*whatsapp "good_bot";
# 恶意爬虫/扫描器
~*sqlmap "bad_bot";
~*nikto "bad_bot";
~*masscan "bad_bot";
~*zgrab "bad_bot";
~*nmap "bad_bot";
~*nessus "bad_bot";
~*burp "bad_bot";
~*gobuster "bad_bot";
~*dirbuster "bad_bot";
~*wfuzz "bad_bot";
~*crawler4j "bad_bot";
~*scrapy "bad_bot";
~*python-requests "bad_bot";
# 自动化工具
~*curl "tool";
~*wget "tool";
~*python-urllib "tool";
~*java "tool";
~*libwww-perl "tool";
~*httpclient "tool";
}
# 基于行为的检测
map $http_user_agent $suspicious_behavior {
default 0;
~*bot 1;
~*spider 1;
~*crawl 1;
~*scan 1;
~*probe 1;
}
```
### 1.2 验证爬虫真实性
```nginx
server {
listen 80;
server_name example.com;
# 白名单爬虫验证(反向 DNS 验证)
location / {
# 仅对白名单爬虫进行验证
if ($bot_type = "good_bot") {
# 验证 Googlebot
if ($http_user_agent ~* googlebot) {
# 实际生产环境需使用 Lua/NJS 进行反向 DNS 验证
# 此处仅示例
access_log /var/log/nginx/verified_bots.log;
}
}
# 拦截恶意爬虫
if ($bot_type = "bad_bot") {
return 444; # 无响应断开连接
}
# 限制自动化工具
if ($bot_type = "tool") {
limit_req zone=tool_limit burst=1 nodelay;
}
proxy_pass http://backend;
}
}
```
### 1.3 基于行为的 Bot 检测
```nginx
# 定义 Bot 检测区域
limit_req_zone $binary_remote_addr zone=bot_detect:10m rate=1r/s;
server {
# 无 User-Agent 检测
if ($http_user_agent = "") {
return 403;
}
# 高频请求检测
location / {
# 检查请求频率异常
limit_req zone=bot_detect burst=5 nodelay;
# 检测无 referer 的 POST 请求(常见于 CSRF/Bot)
if ($request_method = POST) {
set $post_check 1;
}
if ($http_referer = "") {
set $post_check "${post_check}1";
}
if ($post_check = "11") {
return 403 "Suspicious request detected";
}
# 检测异常 header 组合
if ($http_accept = "") {
return 403;
}
proxy_pass http://backend;
}
}
```
### 1.4 挑战-响应机制(使用 Lua)
```nginx
# 需要 ngx_http_lua_module
server {
location / {
access_by_lua_block {
local bot_type = ngx.var.bot_type
if bot_type == "suspicious" then
-- 发送 JavaScript 挑战
ngx.header.content_type = "text/html"
ngx.say([[
]])
ngx.exit(ngx.HTTP_OK)
end
-- 验证 cookie
local cookies = ngx.var.http_cookie
if not cookies or not cookies:match("js_enabled=1") then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
}
proxy_pass http://backend;
}
}
```
### 1.5 Bot 管理策略表
| 类别 | 特征 | 处理方式 |
|------|------|----------|
| **善意爬虫** | Google/Bing/Baidu 等搜索引擎 | 验证真实性后放行,设置独立 rate limit |
| **监控 Bot** | Uptime 检查、SEO 工具 | IP 白名单,设置宽松的限制 |
| **恶意爬虫** | 扫描器、暴力破解工具 | 立即阻断,记录日志 |
| **自动化工具** | curl/wget/脚本 | 严格限制频率,验证码挑战 |
| **未知 Bot** | 未识别的 User-Agent | 行为分析,可疑则挑战验证 |
---
## 2. WAF 配置深度指南(ModSecurity)
### 2.1 ModSecurity 编译安装
```bash
# 安装依赖
apt-get install -y libpcre3-dev libxml2-dev libcurl4-openssl-dev \
liblua5.3-dev libssl-dev libyajl-dev
# 下载 ModSecurity v3
cd /usr/src
git clone --depth 1 -b v3/master https://github.com/SpiderLabs/ModSecurity
cd ModSecurity
git submodule init
git submodule update
# 编译安装
./build.sh
./configure --with-yajl --with-ssdeep --with-lua --with-maxmind
make -j$(nproc)
make install
# 下载 NGINX 连接器
cd /usr/src
git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx
# 编译 NGINX 模块(需 NGINX 源码)
cd /usr/src/nginx-1.24.0
./configure --add-module=/usr/src/ModSecurity-nginx [其他配置选项]
make modules
```
### 2.2 核心配置
```nginx
# modsecurity.conf - 主配置文件
load_module modules/ngx_http_modsecurity_module.so;
http {
# 全局 ModSecurity 配置
modsecurity on;
modsecurity_rules_file /etc/nginx/modsecurity/modsecurity.conf;
server {
listen 80;
server_name example.com;
location / {
# 可为特定 location 启用/禁用
modsecurity on;
# 内联规则
modsecurity_rules '
SecRuleEngine On
SecRule REQUEST_URI "@contains /admin" \
"id:1000,phase:1,deny,status:403,msg:'Admin access restricted'"
';
proxy_pass http://backend;
}
# 静态资源跳过 WAF
location ~* \\.(jpg|jpeg|png|gif|css|js|woff|woff2)$ {
modsecurity off;
proxy_pass http://backend;
}
}
}
```
### 2.3 ModSecurity 核心规则集(CRS)
```ini
# /etc/nginx/modsecurity/crs-setup.conf
# 规则引擎模式
SecRuleEngine DetectionOnly # 仅检测模式(测试阶段)
# SecRuleEngine On # 阻断模式(生产环境)
# 异常评分阈值
SecAction "id:900001,phase:1,nolog,pass,t:none,setvar:tx.inbound_anomaly_score_threshold=5"
SecAction "id:900002,phase:1,nolog,pass,t:none,setvar:tx.outbound_anomaly_score_threshold=4"
# 检测 Paranoia Level(0-4,越高越严格)
SecAction "id:900003,phase:1,nolog,pass,t:none,setvar:tx.paranoia_level=2"
# 允许特定路径(排除规则)
SecRule REQUEST_URI "@beginsWith /api/webhook" \
"id:1001,phase:1,pass,nolog,ctl:ruleEngine=Off"
# 自定义白名单
SecRule REQUEST_HEADERS:User-Agent "@contains MyInternalApp" \
"id:1002,phase:1,pass,nolog,ctl:ruleRemoveById=913100"
# 自定义规则 - SQL 注入增强
SecRule REQUEST_COOKIES|REQUEST_COOKIES_NAMES|REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* \
"@rx (?i:(?:select\\s*\\*\\s*from|union\\s*select.*from|insert\\s+into|delete\\s+from|drop\\s+table))" \
"id:1003,phase:2,deny,status:403,msg:'SQL Injection Detected',logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}'"
```
### 2.4 规则集加载配置
```nginx
# 在 nginx.conf 中加载 CRS
modsecurity_rules_file /etc/nginx/modsecurity/crs-setup.conf;
modsecurity_rules '
# 加载 CRS 规则
Include /usr/share/modsecurity-crs/crs-setup.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-901-INITIALIZATION.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-903.9001-DRUPAL-EXCLUSION-RULES.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-903.9002-WORDPRESS-EXCLUSION-RULES.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-905-COMMON-EXCEPTIONS.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-910-IP-REPUTATION.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-911-METHOD-ENFORCEMENT.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-912-DOS-PROTECTION.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-913-SCANNER-DETECTION.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-921-PROTOCOL-ATTACK.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-931-APPLICATION-ATTACK-RFI.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-934-APPLICATION-ATTACK-GENERIC.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-944-APPLICATION-ATTACK-JAVA.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf
Include /usr/share/modsecurity-crs/rules/RESPONSE-950-DATA-LEAKAGES.conf
Include /usr/share/modsecurity-crs/rules/RESPONSE-951-DATA-LEAKAGES-SQL.conf
Include /usr/share/modsecurity-crs/rules/RESPONSE-952-DATA-LEAKAGES-JAVA.conf
Include /usr/share/modsecurity-crs/rules/RESPONSE-953-DATA-LEAKAGES-PHP.conf
Include /usr/share/modsecurity-crs/rules/RESPONSE-954-DATA-LEAKAGES-IIS.conf
Include /usr/share/modsecurity-crs/rules/RESPONSE-959-BLOCKING-EVALUATION.conf
Include /usr/share/modsecurity-crs/rules/RESPONSE-980-CORRELATION.conf
Include /usr/share/modsecurity-crs/rules/REQUEST-999-EXCLUSION-RULES-AFTER-CRS.conf
';
```
### 2.5 虚拟补丁配置
```ini
# /etc/nginx/modsecurity/virtual-patches.conf
# CVE-2021-44228 (Log4j)
SecRule REQUEST_HEADERS|REQUEST_HEADERS_NAMES|REQUEST_BODY|ARGS|ARGS_NAMES \
"@rx \\$\\{.*jndi:(ldap|ldaps|dns|rmi|iiop|http|https|corba|nis|nds)" \
"id:2001,phase:2,deny,status:403,msg:'CVE-2021-44228 Log4j RCE attempt',logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}'"
# CVE-2017-9805 (Struts REST XStream)
SecRule REQUEST_HEADERS:Content-Type "@contains application/x-www-form-urlencoded" \
"id:2002,phase:1,deny,status:403,msg:'CVE-2017-9805 Struts REST XStream',chain"
SecRule REQUEST_BODY "@contains &1 | grep -oP 'nginx/\\K[0-9.]+')
LATEST=$(curl -s https://nginx.org/en/download.html | grep -oP 'Stable version.*?nginx-[0-9.]+' | grep -oP '[0-9.]+$' | head -1)
if [ "$CURRENT" != "$LATEST" ]; then
echo "警告: NGINX 版本 $CURRENT 需要更新到 $LATEST"
# 发送通知...
fi
```
### 6.3 安全更新流程
```bash
# 1. 备份当前配置
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.backup.$(date +%Y%m%d)
tar -czf /backup/nginx-config-$(date +%Y%m%d).tar.gz /etc/nginx/
# 2. 测试新配置
nginx -t
# 3. 平滑升级(热升级)
# 下载并编译新版本
make upgrade
# 或使用包管理器
apt-get update && apt-get install --only-upgrade nginx
# 4. 验证升级
nginx -v
systemctl status nginx
# 5. 回滚准备(保留旧二进制)
mv /usr/sbin/nginx /usr/sbin/nginx.old
```
---
## 7. 安全审计日志配置
### 7.1 安全审计日志格式
```nginx
# /etc/nginx/conf.d/audit-log.conf
# 安全审计专用日志格式
log_format security '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time" '
'ssl_protocol=$ssl_protocol ssl_cipher=$ssl_cipher '
'req_id=$request_id '
'xff=$http_x_forwarded_for '
'real_ip=$http_x_real_ip '
'cc=$geoip_country_code '
'city=$geoip_city ';
# 攻击检测日志格式
log_format attack '$time_iso8601|$remote_addr|$request_method|$request_uri|'
'$status|$http_user_agent|$http_referer|'
'$request_time|$upstream_response_time|'
'$http_x_forwarded_for|$geoip_country_code|'
'$connection_requests|$msec';
# 安全事件日志
log_format security_event 'time=$time_iso8601 '
'client=$remote_addr '
'method=$request_method '
'uri="$request_uri" '
'status=$status '
'request_length=$request_length '
'body_bytes_sent=$body_bytes_sent '
'referer="$http_referer" '
'user_agent="$http_user_agent" '
'cookie=$http_cookie '
'authorization=$http_authorization '
'content_type=$content_type '
'content_length=$content_length '
'host=$host '
'server_name=$server_name '
'request_id=$request_id '
'ssl_session_id=$ssl_session_id '
'ssl_session_reused=$ssl_session_reused';
```
### 7.2 条件日志记录
```nginx
http {
# 仅记录特定状态码
map $status $loggable {
~^[23] 0; # 2xx/3xx 不记录
default 1; # 其他记录
}
# 仅记录慢请求
map $request_time $slow_request {
~^[0-2]\\. 0; # < 3s 不记录
default 1; # >= 3s 记录
}
# 合并条件
map "${loggable}${slow_request}" $security_log {
"00" 0;
default 1;
}
server {
access_log /var/log/nginx/security.log security if=$security_log;
location / {
proxy_pass http://backend;
}
}
}
```
### 7.3 安全事件过滤
```nginx
# 记录特定安全事件
map "$status:$request_method:$http_user_agent" $security_event {
default 0;
# 记录所有 401/403
"~^401" 1;
"~^403" 1;
"~^404" 1;
# 记录攻击特征
"~*sqlmap" 1;
"~*nikto" 1;
"~*nmap" 1;
# 记录 POST 到敏感路径
"~*:POST:.*/(admin|config|setup|install|phpmyadmin)" 1;
}
server {
access_log /var/log/nginx/security-events.log attack if=$security_event;
# 错误日志分离
error_log /var/log/nginx/security-error.log warn;
}
```
### 7.4 日志轮转与保留
```bash
# /etc/logrotate.d/nginx-security
/var/log/nginx/security*.log {
daily
missingok
rotate 90
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 $(cat /var/run/nginx.pid)
fi
endscript
}
# 攻击日志长期保留
/var/log/nginx/attack*.log {
weekly
missingok
rotate 520 # 保留 10 年
compress
delaycompress
create 0640 www-data adm
sharedscripts
postrotate
kill -USR1 $(cat /var/run/nginx.pid)
endscript
}
```
### 7.5 实时安全监控脚本
```bash
#!/bin/bash
# /usr/local/bin/nginx-security-monitor.sh
LOG_FILE="/var/log/nginx/security.log"
ALERT_THRESHOLD=100 # 每分钟请求数阈值
BLOCK_THRESHOLD=10 # 触发封禁的异常请求数
# 实时监控
tail -F $LOG_FILE | while read line; do
# 提取 IP
ip=$(echo "$line" | awk '{print $1}')
# 检查攻击模式
if echo "$line" | grep -qE "(union|select|script|alert|eval\\(|system\\()"; then
echo "$(date): SQL/XSS 攻击检测: $ip - $line" >> /var/log/nginx/alerts.log
# 可选:自动添加到黑名单
# echo "deny $ip;" >> /etc/nginx/conf.d/auto-block.conf
fi
# 检查扫描行为
if echo "$line" | grep -qE "(\\.env|\\.git|config\\.xml|phpmyadmin|wp-admin)"; then
echo "$(date): 敏感路径扫描: $ip - $line" >> /var/log/nginx/alerts.log
fi
done
```
---
## 8. 完整安全配置示例
### 8.1 生产环境安全配置
```nginx
# /etc/nginx/nginx.conf - 安全强化主配置
user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
# 加载动态模块
load_module modules/ngx_http_geoip_module.so;
load_module modules/ngx_http_modsecurity_module.so;
events {
worker_connections 4096;
use epoll;
multi_accept on;
}
http {
# 基础设置
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 隐藏版本号
server_tokens off;
# 字符编码
charset utf-8;
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';
log_format security '$remote_addr - $time_iso8601 - "$request" - $status - '
'"$http_user_agent" - "$http_referer" - '
'req_time=$request_time - xff="$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
# 性能优化
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
# 客户端限制
client_max_body_size 10m;
client_body_buffer_size 128k;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
client_body_timeout 12s;
client_header_timeout 12s;
# 缓冲区设置
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
# GeoIP
geoip_country /usr/share/GeoIP/GeoIP.dat;
geoip_city /usr/share/GeoIP/GeoLiteCity.dat;
# 限流区域
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=50r/s;
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=1r/m;
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
# Bot 检测
map $http_user_agent $bot_type {
default "human";
~*googlebot "good_bot";
~*bingbot "good_bot";
~*sqlmap "bad_bot";
~*nikto "bad_bot";
~*nmap "bad_bot";
~*curl "tool";
~*wget "tool";
}
# 国家代码映射
map $geoip_country_code $allowed_country {
default yes;
CN yes;
US yes;
JP yes;
RU no;
KP no;
IR no;
}
# 包含其他配置
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
```
### 8.2 HTTPS 站点安全配置
```nginx
# /etc/nginx/sites-available/example.com
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# SSL 证书
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
# SSL 配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
# SSL 会话
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/example.com.chain.crt;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# 安全响应头
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https:; connect-src 'self' https://api.example.com; frame-ancestors 'self'; form-action 'self'; base-uri 'self';" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;
# 根目录
root /var/www/example.com;
index index.html index.htm;
# 日志
access_log /var/log/nginx/example.com-access.log main;
error_log /var/log/nginx/example.com-error.log warn;
# 全局限制
limit_conn conn_limit 20;
limit_req zone=req_limit burst=20 nodelay;
# 安全过滤
if ($bad_ip) {
return 444;
}
if ($allowed_country = no) {
return 403;
}
# 敏感文件保护
location ~ /\\. {
deny all;
return 404;
}
location ~* \\.(git|svn|env|config|log|sql|backup|bak|swp|old|orig|save)$ {
deny all;
return 404;
}
# 静态资源
location ~* \\.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf|eot|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header X-Frame-Options "SAMEORIGIN" always;
access_log off;
}
# API 路由
location /api/ {
limit_req zone=api_limit burst=50 nodelay;
# 内容类型验证
if ($content_type !~ ^(application/json|application/x-www-form-urlencoded|multipart/form-data)$) {
return 415;
}
# 方法限制
if ($request_method !~ ^(GET|POST|PUT|DELETE|PATCH|OPTIONS)$) {
return 405;
}
# CORS
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin "https://example.com" always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, PATCH, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With" always;
add_header Access-Control-Allow-Credentials "true" always;
add_header Access-Control-Max-Age 86400 always;
return 204;
}
add_header Access-Control-Allow-Origin "https://example.com" always;
add_header Access-Control-Allow-Credentials "true" always;
proxy_pass http://api_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Request-ID $request_id;
}
# 认证路由
location /auth/ {
limit_req zone=login_limit burst=3 nodelay;
proxy_pass http://auth_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# 主应用
location / {
try_files $uri $uri/ /index.html;
# 安全响应头(静态页面)
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
}
}
```
### 8.3 WAF 完整配置
```nginx
# /etc/nginx/conf.d/waf.conf
# ModSecurity 全局配置
modsecurity on;
modsecurity_rules_file /etc/nginx/modsecurity/main.conf;
# 特定路径规则
server {
location / {
# 启用完整 CRS
modsecurity_rules '
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyLimit 524288
SecResponseBodyLimitAction ProcessPartial
# 文件上传限制
SecRequestBodyLimit 13107200
SecRequestBodyNoFilesLimit 131072
# 允许文件上传类型
SecRule REQUEST_FILENAME "@rx \\.(jpg|jpeg|png|gif|pdf|doc|docx)$" \
"id:2000,phase:1,pass,nolog,ctl:requestBodyLimit=52428800"
# 加载 CRS
Include /usr/share/modsecurity-crs/crs-setup.conf
Include /usr/share/modsecurity-crs/rules/*.conf
# 自定义排除
SecRule REQUEST_URI "@beginsWith /api/webhook" \
"id:9000,phase:1,pass,nolog,ctl:ruleRemoveById=920350"
';
proxy_pass http://backend;
}
# 静态资源跳过 WAF
location ~* \\.(jpg|jpeg|png|gif|css|js|woff|woff2|ttf|eot|svg|ico)$ {
modsecurity off;
expires 1y;
access_log off;
proxy_pass http://backend;
}
}
```
### 8.4 安全配置检查清单
```markdown
## 部署前检查清单
### 基础安全
- [ ] 使用最新稳定版 NGINX
- [ ] server_tokens 设置为 off
- [ ] 禁用不安全的 SSL 协议(SSLv2/SSLv3/TLSv1.0/TLSv1.1)
- [ ] 配置安全的加密套件
- [ ] 启用 HSTS
- [ ] 配置所有安全响应头
### 访问控制
- [ ] 管理后台 IP 白名单
- [ ] 敏感文件/目录访问限制
- [ ] 地理位置访问控制(如需要)
- [ ] User-Agent 过滤
### 请求限制
- [ ] 请求速率限制(通用)
- [ ] API 专用限流
- [ ] 登录接口严格限流
- [ ] 连接数限制
- [ ] 带宽限制
### WAF 防护
- [ ] ModSecurity 安装配置
- [ ] CRS 规则集加载
- [ ] 虚拟补丁规则
- [ ] 自定义业务规则
- [ ] 排除规则测试
### 日志监控
- [ ] 安全审计日志配置
- [ ] 日志轮转设置
- [ ] 实时监控脚本
- [ ] fail2ban 集成
### 性能与安全平衡
- [ ] 静态资源缓存策略
- [ ] 安全头对静态资源的影响
- [ ] WAF 对性能的损耗评估
- [ ] 限流阈值合理性测试
### 测试验证
- [ ] SSL Labs A+ 评分
- [ ] Security Headers 检查
- [ ] OWASP ZAP 扫描
- [ ] 渗透测试
- [ ] 性能基准测试
```
---
## 附录:常用安全测试命令
```bash
# SSL/TLS 测试
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3
testssl.sh example.com
# 安全响应头检查
curl -I -s https://example.com | grep -E "^(Strict-Transport-Security|Content-Security-Policy|X-Frame-Options)"
# 漏洞扫描基础
curl -s https://example.com/.env && echo "DANGER: .env exposed!"
curl -s https://example.com/.git/HEAD && echo "DANGER: .git exposed!"
curl -s https://example.com/config.xml && echo "DANGER: config exposed!"
# 压力测试(限流验证)
ab -n 1000 -c 100 https://example.com/api/test
siege -c 50 -t 30s https://example.com/
```