安全加固实战

安全加固实战

写在前面

Nginx 作为反向代理,经常直接面对公网——所有 HTTP 攻击都先打 Nginx。本篇会讲清楚 Nginx 自身的常见安全配置:防 CC、防 SQL 注入、防路径穿越、限流、防爬虫,以及 ModSecurity WAF 的集成,最后实战公司 API 网关被攻击的真实案例。

一、安全头(基础)

任何 Nginx 服务都必须设置的几个头:

server {
    # 防止 MIME 类型嗅探
    add_header X-Content-Type-Options "nosniff" always;

    # 防止点击劫持
    add_header X-Frame-Options "SAMEORIGIN" always;

    # XSS 过滤器
    add_header X-XSS-Protection "1; mode=block" always;

    # CSP - 内容安全策略
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;" always;

    # Referrer
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Permissions Policy(替代 Feature-Policy)
    add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

    # HSTS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}

1.1 每个头的含义

1.2 always 关键字

always 让头在所有响应都加上(默认 4xx/5xx 不加)。生产环境永远加 always。

二、限流

2.1 限制请求速率(limit_req)

# http 块定义限流区
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;

server {
    location /api/ {
        limit_req zone=api_limit burst=200 nodelay;
        proxy_pass http://backend;
    }
}

2.2 限制连接数(limit_conn)

limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

server {
    location /download/ {
        limit_conn conn_limit 5;     # 每个 IP 最多 5 个并发连接
        proxy_pass http://backend;
    }
}

2.3 限制下载速度

location /download/ {
    limit_rate_after 10m;     # 前 10MB 满速
    limit_rate 1m;             # 之后 1MB/s
}

防止单个下载占满带宽。

2.4 多维限流

基于用户登录态的限流:

# 用 map 把 user ID 转成限流 key
map $cookie_session $limit_key {
    default $binary_remote_addr;     # 未登录用 IP
    ~.+ $cookie_session$binary_remote_addr;  # 登录用 session + IP
}

limit_req_zone $limit_key zone=user_limit:10m rate=10r/s;

server {
    location /api/ {
        limit_req zone=user_limit burst=20 nodelay;
    }
}

2.5 实战:被攻击时的多级限流

公司 API 网关被攻击时,我们用了多级漏斗:

# 第一级:全局限流(防整体带宽耗尽)
limit_req_zone $binary_remote_addr zone=global_limit:10m rate=1000r/s;

# 第二级:按 IP 限流(防单 IP 攻击)
limit_req_zone $binary_remote_addr zone=ip_limit:10m rate=50r/s;

# 第三级:按 API 限流(防单个接口被打)
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=5r/s;

server {
    # 全局
    limit_req zone=global_limit burst=2000 nodelay;

    location /api/login {
        limit_req zone=login_limit burst=10 nodelay;  # 登录接口特别严
        proxy_pass http://user_service;
    }

    location /api/ {
        limit_req zone=ip_limit burst=100 nodelay;
        proxy_pass http://backend;
    }
}

效果:单 IP 被攻击时只影响自己的流量,其他用户不受影响。

三、防 CC 攻击

3.1 什么是 CC 攻击

CC(Challenge Collapsar)攻击:用大量真实 IP(可能是肉鸡)对特定 URL 发起大量请求,耗尽后端资源。

特点:

3.2 实战:被攻击的真实案例

时间:2024 年 7 月某个周日 现象:API 网关 CPU 100%,所有接口 P99 延迟 30 秒 日志:某 IP 段对 /api/products/search 发起每秒 5000+ 请求

3.3 第一阶段:紧急止血

# 立即给被攻击的接口加严苛限流
location /api/products/search {
    limit_req zone=search_limit burst=20 nodelay;
    proxy_pass http://product_service;
}

limit_req_zone $binary_remote_addr zone=search_limit:10m rate=5r/s;

5 分钟内恢复。

3.4 第二阶段:加验证码

被攻击的接口加图形验证码:

location /api/products/search {
    # 验证 Referer
    if ($http_referer !~ "^https://company.com") {
        return 403;
    }

    # 验证 Cookie(用户必须先访问首页拿到 cookie)
    if ($cookie_verified != "yes") {
        return 302 https://company.com/captcha;
    }

    proxy_pass http://product_service;
}

加验证码是终极武器——CC 攻击最大的问题是请求"合法",但加了验证码就逼攻击者做 OCR,攻击成本飙升。

3.5 第三阶段:接入云 WAF

server {
    # 把请求转给云 WAF
    location / {
        proxy_pass https://waf-api.company.com$request_uri;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

阿里云 WAF、Cloudflare 都提供类似的反代模式。

3.6 防 CC 的多层防线

[Cloudflare WAF]        ← 7 层过滤
       ↓
[Nginx limit_req]       ← 粗粒度限流
       ↓
[行为分析]              ← 检测异常 IP
       ↓
[验证码]                ← 终极防御
       ↓
[后端]

四、防 SQL 注入

4.1 Nginx 层能做的有限

Nginx 不是 WAF,不能在 Nginx 配置里写 SQL 注入规则(那需要正则解析 SQL 语法)。

4.2 但可以过滤常见攻击 payload

location / {
    # 拦截常见 SQL 注入关键词
    if ($request_uri ~* "(union|select|insert|update|delete|drop|truncate|xp_cmdshell|benchmark)") {
        return 403;
    }

    # 拦截常见路径穿越
    if ($request_uri ~* "(\.\./|\.\.\\|%2e%2e|%252e%252e)") {
        return 403;
    }

    proxy_pass http://backend;
}

⚠️ 这只是粗过滤,真正的 SQL 注入防御在后端(参数化查询、ORM)。

4.3 用 ModSecurity

ModSecurity 是真正的 WAF,可以解析 HTTP 协议,匹配 OWASP 规则集。

# 安装
apt install libmodsecurity3
# 编译 nginx-modsecurity 或用 OpenResty

# nginx.conf 加载
load_module modules/ndk_http_module.so;
load_module modules/ngx_http_modsecurity_module.so;

server {
    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsecurity/main.conf;
    location / {
        proxy_pass http://backend;
    }
}

modsecurity/main.conf 启用 OWASP CRS:

SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecRule REQUEST_HEADERS:Content-Type "text/xml" \
  "id:200000, phase:1, deny, status:403, log, msg:'XML request blocked'"

Include /etc/nginx/modsecurity/owasp-crs/crs-setup.conf
Include /etc/nginx/modsecurity/owasp-crs/rules/*.conf

我们生产用了 ModSecurity + OWASP CRS,有效拦截了 90%+ 的自动化攻击。

五、路径穿越与文件包含

5.1 攻击原理

# 攻击者请求
GET /../../../etc/passwd HTTP/1.1
GET /static/..%2F..%2F..%2Fetc%2Fpasswd

5.2 防护

server {
    # 拦截路径穿越
    if ($request_uri ~* "(\.\.|%2e%2e|%252e)") {
        return 403;
    }

    # 关键 - alias 必须有结尾 /
    location /static/ {
        alias /var/www/static/;
    }
    # NG - 没有结尾 / 会让 /static../etc/passwd 穿过
    # location /static {
    #     alias /var/www/static;
    # }

    # 文件下载目录 - 禁止执行
    location /uploads/ {
        types {} default_type application/octet-stream;
        root /var/www;
    }
}

5.3 关闭不必要的方法

# 只允许 GET / POST / HEAD
if ($request_method !~ ^(GET|POST|HEAD)$) {
    return 405;
}

六、隐藏 Nginx 信息

server_tokens off;  # 隐藏版本号

more_clear_headers Server;  # 用 headers-more 模块,完全删 Server 头

攻击者拿到版本号就能针对性找漏洞。绝对要隐藏。

七、防爬虫

7.1 基于 User-Agent

location / {
    if ($http_user_agent ~* "(scrapy|httpclient|scanner|crawler|spider)") {
        return 403;
    }
    proxy_pass http://backend;
}

⚠️ 简单的 UA 过滤很容易被绕过(User-Agent 可任意伪造)。

7.2 行为分析(更可靠)

用 OpenResty + Lua:

-- 每分钟每个 IP 的请求数
local limit = require "resty.limit.req"
local lim = limit.new("user_rate", 100, 100)  -- 100 req/s, 100 burst

local key = ngx.var.binary_remote_addr
local delay, err = lim:incoming(key, true)

if not delay then
    if err == "rejected" then
        return ngx.exit(503)
    end
end

7.3 robots.txt

location = /robots.txt {
    root /var/www/static;
}

robots.txt 是君子协议,真正的恶意爬虫不遵守。

八、IP 黑名单

8.1 临时封禁

# 黑名单文件
include /etc/nginx/blacklist.conf;

# blacklist.conf 内容
deny 1.2.3.4;
deny 5.6.7.0/24;

8.2 动态黑名单

# 用 geo 模块从变量里读
geo $is_blacklisted {
    default 0;
    1.2.3.4 1;
    5.6.7.0/24 1;
}

server {
    if ($is_blacklisted) {
        return 403;
    }
}

$is_blacklisted 可以从外部文件动态加载:

# /etc/nginx/conf.d/blacklist-geo.conf
geo $is_blacklisted {
    default 0;
    include /etc/nginx/blacklist-ip.conf;
}

脚本自动添加恶意 IP:

#!/bin/bash
echo "deny $1;" >> /etc/nginx/blacklist-ip.conf
nginx -s reload

8.3 实战:被攻击时的紧急封禁

# 看访问最多的 IP
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

# 封禁
echo "deny 1.2.3.4;" >> /etc/nginx/blacklist.conf
nginx -t && nginx -s reload

九、安全审计清单

9.1 上线前必查

9.2 定期检查

十、本篇小结

攻击类型 防御手段
CC 攻击 多级 limit_req + 验证码 + WAF
SQL 注入 Nginx 粗过滤 + WAF + 后端参数化
路径穿越 Nginx location + alias + 过滤
爬虫 UA 过滤 + 行为分析 + robots.txt
暴力破解 单独接口限流 + 验证码
带宽耗尽 limit_rate + CDN

下一篇讲 性能调优与高并发,怎么把单机 Nginx 调到 10w 并发。

参考资料