LOGO 首页 OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 技术文档 其他文档  
 
网站管理员

Nginx 安全加固——限速 / 封 IP / 防 DDoS 基础

admin
2026年7月19日 14:2 本文热度 35

服务器只要暴露在公网上,就会有爬虫扫描、暴力破解、CC 攻击、恶意爬虫。Nginx 默认配置是"能跑就行",完全没有防护。这篇就来聊安全加固,核心三板斧:限速防刷、封禁恶意 IP、防 DDoS 基础

💡 提示: 安全加固的原则是"最小暴露面"——能不暴露的端口就不暴露,能不给的信息就不给,能不开放的路径就不开放。每减少一个暴露面,攻击面就少一个。

一、隐藏敏感信息

1.1 为什么要隐藏信息

攻击者在攻击之前会先"踩点"——探测服务器版本、模块、操作系统。信息越少,攻击成本越高。Nginx 默认会在错误页面和响应头里暴露版本号:

# 默认的响应头
Server: nginx/1.24.0

# 默认的 404 页面
404 Not Found
nginx/1.24.0

攻击者看到 nginx/1.24.0,就能精准查找这个版本的已知漏洞。

1.2 隐藏版本号

# /etc/nginx/nginx.conf 的 http 块里

http {
    server_tokens off;
}

改完后效果:

项目
server_tokens on(默认)
server_tokens off
响应头
Server: nginx/1.24.0Server: nginx
错误页面
底部显示 nginx/1.24.0
底部只显示 nginx
暴露信息
版本号 + 模块信息
仅服务器名称

⚠️ 注意: server_tokens off 只是隐藏版本号,不是真正的安全措施。它增加的是攻击成本,不是安全性本身。真正的安全是及时更新 Nginx 版本和系统补丁。

1.3 完全隐藏 Server 头

server_tokens off 还会暴露 Server: nginx,如果想完全去掉:

# 需要安装 headers-more 模块
sudo apt install nginx-extras
# 包含了 headers-more 模块,或从源码编译时添加 --add-module 参数。

# 在 nginx.conf 的 http 块里
http {
    more_clear_headers "Server";
    more_clear_headers "X-Powered-By";
    more_clear_headers "X-Runtime";
}
方法
效果
需要额外模块
server_tokens off
隐藏版本号,保留 Server: nginx
不需要
more_clear_headers
完全去掉 Server 头
headers-more
编译时修改源码 src/http/ngx_http_header_filter_module.c
改掉默认 Server 字符串
不需要(但需要编译)

1.4 添加安全响应头

# /etc/nginx/conf.d/example.conf 的 server 块里

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

    # 防止点击劫持(页面不允许被 iframe 嵌套)
    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;

    # HSTS——强制浏览器以后都用 HTTPS 访问
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # Referrer 策略——控制 Referer 头泄露
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # 权限策略——控制浏览器功能(摄像头/麦克风/地理位置等)
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
}
安全头
作用
推荐值
X-Content-Type-Options
防止浏览器猜测文件类型
nosniff
X-Frame-Options
防止页面被 iframe 嵌套
SAMEORIGIN
X-XSS-Protection
启用浏览器 XSS 过滤
1; mode=block
Content-Security-Policy
限制资源加载来源
按需配置
Strict-Transport-Security
强制 HTTPS(HSTS)
max-age=31536000
Referrer-Policy
控制 Referer 泄露
strict-origin-when-cross-origin
Permissions-Policy
禁用不需要的浏览器功能
按需配置

💡 提示: always 参数很重要。不加 always 时,这些头只在 2xx/3xx 响应中添加;加了 always 后,4xx/5xx 错误页面也会带上安全头。攻击者经常故意请求错误页面来探测信息。

1.5 隐藏不必要的错误信息

# 不要把后端错误暴露给用户
location /api/ {
    proxy_pass http://127.0.0.1:3000;

    # 后端返回 500 时,显示自定义错误页而不是堆栈信息
    proxy_intercept_errors on;
    error_page 500 502 503 504 /50x.html;
}

# 自定义错误页面
location = /50x.html {
    root /var/www/errors;
    internal;
}

二、限速——limit_req 模块

2.1 为什么限速

不限速时,一个 IP 一秒发 1000 个请求,Nginx 全部转发到后端,后端直接被打满。限速就是给每个 IP 设一个请求频率上限,超过的请求直接拒绝。

攻击类型
不限速时
限速后
CC 攻击
每秒 1000 请求打满后端
每秒只放 10 个,其余 503
暴力扫描目录
爬虫秒级扫完所有路径
每秒 2 个请求,扫描成本翻 500 倍
恶意爬虫
疯狂抓取内容
限制到人类正常速度
API 滥用
恶意刷接口
超出频率直接拒绝

2.2 limit_req_zone 定义区域

# /etc/nginx/nginx.conf 的 http 块里

http {
    # 定义限速区域
    # $binary_remote_addr  以二进制格式存储客户端 IP(比文本省内存)
    # zone=req_limit:10m   共享内存区域名和大小,10MB 约可存储十万级 IP 的状态(取决于实际请求模式)
    # rate=10r/s           每个 IP 每秒最多 10 个请求

    limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
}

💡 提示: $binary_remote_addr 比 $remote_addr 省内存。文本格式的 IPv4 地址占 15 字节(如 192.168.1.100),二进制格式只占 4 字节。10MB 内存约可存 16 万个 IP 的状态。

2.3 limit_req 启用限速

# /etc/nginx/conf.d/example.conf

server {
    # 全局限速(所有 location 共享)
    limit_req zone=req_limit burst=20 nodelay;

    # 只对 API 限速
    location /api/ {
        limit_req zone=req_limit burst=20 nodelay;
        proxy_pass http://127.0.0.1:3000;
    }

    # 登录接口更严格
    location /api/login {
        limit_req zone=req_limit burst=5 nodelay;
        proxy_pass http://127.0.0.1:3000;
    }
}

2.4 burst 和 nodelay 详解

这是限速配置最容易搞混的部分:

参数
作用
不加的后果
burst=20
允许突发 20 个请求排队等待
超过 rate 的请求直接 503
nodelay
排队的请求不延迟,立即处理
排队请求会延迟处理,用户体验差

各种组合的效果:

配置
行为
rate=10r/s
(无 burst)
严格每秒 10 个,第 11 个立即 503
rate=10r/s burst=20
突发 30 个(10+20)能进入队列,但队列中的请求要等,超出 503
rate=10r/s burst=20 nodelay
突发 30 个立即处理不延迟,超出 503(推荐
rate=10r/s burst=20 delay=10
前 10 个立即处理,后 10 个延迟处理,超出 503
# 图解:rate=10r/s burst=20 nodelay 的效果
#
# 时间轴(秒)
# 0s:  30个请求同时来 → 前30个通过(10常规+20突发),第31个503
# 1s:  又来5个 → 全部通过(桶里还有10个额度)
# 2s:  又来15个 → 全部通过(用完突发桶)
# 3s:  又来15个 → 前10个通过,后5个503(突发桶空了)

⚠️ 注意: burst 设太大会让限速形同虚设。一般 API 接口 burst 设 5-20,普通页面设 20-50。登录等敏感接口 burst 设 3-5。简单记忆:burst 是‘突发窗口’,nodelay 是‘不要排队’。两者结合就是‘允许你偶尔超一点,但不让你的请求等待。

2.5 自定义限速响应

默认被限速时返回 503 Service Unavailable,可以改成 429 Too Many Requests(更符合 HTTP 语义):

http {
    limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
    limit_req_status 429;
}

server {
    limit_req zone=req_limit burst=20 nodelay;

    # 自定义 429 响应
    error_page 429 = @rate_limited;

    location @rate_limited {
        default_type application/json;
        return 429 '{"error": "Too Many Requests", "retry_after": 60}';
    }
}

2.6 多维度限速

不同接口限速不同:

http {
    # 全局限速:每秒 10 个请求
    limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;

    # API 限速:每秒 5 个请求
    limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s;

    # 登录限速:每秒 1 个请求
    limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;

    # 下载限速:每秒 2 个请求
    limit_req_zone $binary_remote_addr zone=download:10m rate=2r/s;
}

server {
    # 全局限速
    limit_req zone=general burst=20 nodelay;

    location /api/ {
        limit_req zone=api burst=10 nodelay;
        proxy_pass http://127.0.0.1:3000;
    }

    location /api/login {
        limit_req zone=login burst=3 nodelay;
        proxy_pass http://127.0.0.1:3000;
    }

    location /download/ {
        limit_req zone=download burst=5 nodelay;
        proxy_pass http://127.0.0.1:3000;
    }
}
接口类型
rate
burst
说明
普通页面
10r/s
20
正常浏览
API 接口
5r/s
10
前端轮询
登录接口
1r/s
3
防暴力破解
下载接口
2r/s
5
防带宽滥用
搜索接口
3r/s
5
防恶意搜索

三、限制并发连接数——limit_conn 模块

3.1 limit_req vs limit_conn

模块
限制维度
适用场景
limit_req
请求频率(每秒几次)
CC 攻击、API 滥用
limit_conn
并发连接数(同时几个)
慢速攻击、连接耗尽

limit_req 限制"每秒发多少个请求",limit_conn 限制"同时保持多少个连接"。两者互补,都要开。

3.2 配置 limit_conn

# http 块
http {
    # 定义连接数限制区域
    # 每个IP最多同时保持 10 个连接
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
}

# server 块
server {
    # 每个IP最多 10 个并发连接
    limit_conn conn_limit 10;

    # 整个虚拟主机最多 1000 个并发连接(防止单站占满所有连接)
    limit_conn_zone $server_name zone=perserver:10m;

    limit_conn perserver 1000;

    limit_conn_status 429;
}

3.3 慢速攻击防护

慢速攻击(Slowloris)的原理是:攻击者建立大量连接,但每个请求都发得极慢(比如每分钟发一个字节),占满服务器的连接数,正常用户连不上。

攻击类型
原理
Nginx 防护
Slowloris
大量半连接,请求头发得极慢
limit_conn + 超时配置
Slow POST
发大 body 但传得极慢
client_body_timeout
Slow Read
读取响应极慢
client_header_timeout
# http 块——超时配置
http {
    # 客户端请求头超时(60秒没发完请求头就断开)
    client_header_timeout 60s;

    # 客户端请求体超时(60秒没发完 body 就断开)
    client_body_timeout 60s;

    # 客户端响应超时(发送响应后60秒客户端没读完就断开)
    send_timeout 60s;

    # keepalive 超时(空闲连接65秒后断开)
    keepalive_timeout 65s;

    # 限制请求体大小(防止超大 POST 攻击)
    client_max_body_size 10m;

    # 限制请求头大小
    large_client_header_buffers 4 8k;
}
超时参数
默认值
推荐值
说明
client_header_timeout
60s
30s
请求头发送超时
client_body_timeout
60s
30s
请求体发送超时
send_timeout
60s
30s
响应发送超时
keepalive_timeout
75s
30-65s
空闲连接超时
client_max_body_size
1m
按需设置
请求体最大大小

四、IP 封禁——allow / deny

4.1 基本用法

# 允许特定 IP,拒绝其他所有
location /admin {
    allow 192.168.1.0/24;     # 允许内网
    allow 10.0.0.0/8;         # 允许 VPN
    allow 123.45.67.89;       # 允许特定公网 IP
    deny all;                 # 拒绝其他所有

    proxy_pass http://127.0.0.1:3000;
}

# 拒绝特定 IP,允许其他所有
location / {
    deny 1.2.3.4;             # 拒绝单个 IP
    deny 5.6.0.0/16;          # 拒绝一个网段
    allow all;                # 允许其他所有

    proxy_pass http://127.0.0.1:3000;
}

⚠️ 注意: allow 和 deny 按从上到下顺序匹配,匹配到就停止。所以 deny all 一定要放在最后,allow all 一定要放在最后(取决于策略)。

4.2 适用场景

场景
策略
后台管理页面
allow 内网 + VPN,deny all
内部 API
allow 内网,deny all
封禁已知恶意 IP
deny 恶意IP,allow all
只允许特定国家访问
allow 国家IP段,deny all(需要 GeoIP)

4.3 全局 IP 封禁

# /etc/nginx/conf.d/blocklist.conf

# 封禁列表(可以单独维护一个文件,方便更新)
deny 1.2.3.4;        # 扫描器
deny 5.6.7.8;        # 暴力破解
deny 10.20.30.0/24;  # 某机房段

# 在 server 块里引用
server {
    include /etc/nginx/conf.d/blocklist.conf;
    # ... 其他配置
}

五、Geo 模块——条件封禁

5.1 为什么用 geo 模块

allow/deny 适合封少数 IP。如果要根据 IP 段做不同的策略(比如国内放行、国外限制),手动写 allow/deny 列表会非常长。geo 模块可以给 IP 段打标签,然后在配置里根据标签做不同的处理。

5.2 geo 基本用法

# http 块
http {
    # 定义 IP 分类
    geo $allowed_ip {
        default        0;     # 默认不允许
        192.168.0.0/16 1;     # 内网允许
        10.0.0.0/8     1;     # VPN允许
        123.45.67.89   1;     # 特定IP允许
    }
}

# server 块
server {
    location /admin {
        if ($allowed_ip = 0) {
            return 403;
        }
        proxy_pass http://127.0.0.1:3000;
    }
}

5.3 封禁恶意 IP 段

http {
    geo $blocked_ip {
        default 0;  # 默认不封禁

        # 已知攻击源
        1.2.3.0/24    1;
        5.6.0.0/16    1;
        10.20.30.0/24 1;

        # 某云扫描器段
        100.200.0.0/16 1;
    }
}

server {
    if ($blocked_ip = 1) {
        return 403;
    }
}

5.4 使用 map 实现灵活控制

http {
    # 定义封禁列表
    geo $is_blocked {
        default 0;
        1.2.3.0/24    1;
        5.6.0.0/16    1;
    }

    # 用 map 定义不同 location 的封禁行为
    map $is_blocked$uri $block_action {
        default        0;    # 不封禁
        1/api/login    1;    # 封禁访问登录接口
        1/admin        1;    # 封禁访问后台
    }
}

server {
    location / {
        if ($block_action = 1) {
            return 403;
        }
        proxy_pass http://127.0.0.1:3000;
    }
}

💡 提示: geo 模块的查找是 O(1) 的(基于 radix tree),性能远好于正则匹配。大量 IP 段的封禁建议用 geo,不要用 if + 正则。

六、限制 HTTP 方法

6.1 只允许必要的方法

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

    # API 允许 GET、POST、PUT、DELETE、HEAD
    location /api/ {
        if ($request_method !~ ^(GET|POST|PUT|DELETE|HEAD)$ ) {
            return 405;
        }
        proxy_pass http://127.0.0.1:3000;
    }
}
方法
用途
是否需要
GET
获取资源
普通网站需要
POST
提交数据
表单/API 需要
HEAD
只取响应头
健康检查需要
PUT
上传/更新资源
RESTful API
DELETE
删除资源
RESTful API
TRACE
调试用
必须禁止
(XST 攻击)
CONNECT
代理隧道
必须禁止
OPTIONS
预检请求
CORS 需要
PATCH
部分更新
RESTful API

⚠️ 注意: TRACE 和 CONNECT 方法在生产环境必须禁止。TRACE 会导致跨站追踪(XST)攻击,CONNECT 可能被用来做代理跳板。

七、防止目录遍历和敏感文件泄露

7.1 禁止访问隐藏文件和敏感路径

server {
    # 禁止访问以 . 开头的隐藏文件(.git、.env、.htaccess 等)
    location ~ /\. {
        deny all;
        access_log off;
        log_not_found off;
    }

    # 禁止访问备份文件
    location ~ ~$ {
        deny all;
        access_log off;
        log_not_found off;
    }

    # 禁止访问 .git 目录(源码泄露)
    location ~ /\.git {
        deny all;
        access_log off;
        log_not_found off;
    }

    # 禁止访问 .env 文件(数据库密码等)
    location ~ /\.env {
        deny all;
        access_log off;
        log_not_found off;
    }

    # 禁止访问配置文件
    location ~ \.(sql|bak|backup|old|swp|tmp|log)$ {
        deny all;
        access_log off;
        log_not_found off;
    }
}

7.2 常见泄露路径

路径
风险
处理方式
/.git/
源码泄露
deny all
/.env
数据库密码、API Key 泄露
deny all
/.htaccess
Apache 配置泄露
deny all
/.htpasswd
密码文件泄露
deny all
/wp-admin/
WordPress 后台
IP 白名单
/phpmyadmin/
数据库管理工具
IP 白名单
/*.sql
数据库备份泄露
deny all
/*.bak
备份文件泄露
deny all
/*.log
日志文件泄露
deny all

🚨 警告: .git 目录泄露是常见的安全事故之一。攻击者通过 /.git/config 可以还原整个源代码仓库,包括数据库密码、API 密钥等。务必确保 /.git 被封禁。

7.3 关闭目录列表

server {
    # 关闭目录列表(如果 index 文件不存在,不显示目录内容)
    autoindex off;
}

默认就是 off,但确认一下没被意外打开。

八、防止图片盗链

8.1 什么是盗链

别人网站直接引用服务器上的图片 URL,用户访问他的网站,图片是从服务器加载的——带宽是你出的,流量是你付的。

8.2 配置防盗链

server {
    # 图片防盗链
    location ~ \.(jpg|jpeg|png|gif|webp|svg|mp4|mp3|woff|woff2)$ {
        # valid_referers:合法的来源
        # none:直接访问(浏览器地址栏输入URL)
        # blocked:Referer 被代理删除了
        # server_names:本站域名
        valid_referers none blocked server_names
            *.example.com example.com
            *.google.com;  # 允许搜索引擎

        if ($invalid_referer) {
            # 返回一张替代图片(或直接 403)
            return 403;
            # 或者返回一张"禁止盗链"的图片
            # rewrite ^/ /hotlink-denied.png last;
        }

        root /var/www/example;
    }
}
valid_referers 参数
含义
none
允许无 Referer(直接访问)
blocked
允许 Referer 被代理删除的请求
server_names
允许 Referer 中的域名匹配 server_name
*.example.com
允许特定域名的子域
example.com
允许特定域名

💡 提示: 如果图片需要被第三方 CDN 加载(比如又拍云、七牛云),把 CDN 域名加到 valid_referers 里。valid_referers 是基础防护,不能完全防止盗链,对高价值资源考虑用签名 URL。

九、防 DDoS 基础

9.1 DDoS 攻击类型

类型
原理
Nginx 能防吗
CC 攻击
大量 HTTP 请求打满后端
✅ limit_req
连接耗尽
大量连接占满 worker
✅ limit_conn
慢速攻击
慢速请求占满连接
✅ 超时配置
SYN Flood
TCP 半连接洪泛
❌ 内核层防护(sysctl)
UDP/ICMP 洪泛
带宽耗尽
❌ 需要 ISP/高防IP
放大攻击
利用 DNS/NTP 放大流量
❌ 需要 ISP/高防IP

⚠️ 注意: Nginx 是应用层(L7)服务器,只能防应用层 DDoS(CC/连接耗尽/慢速攻击)。网络层 DDoS(SYN Flood/UDP 洪泛)需要内核参数调优 + 云服务商高防 IP。Nginx 做不到的事情不要勉强。

9.2 Nginx 层面能做的

第一层:限速 + 限连接

http {
    # 限速
    limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
    # 限连接
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

    # 限制每 IP 请求体大小
    client_body_buffer_size 8k;
    client_max_body_size 2m;

    # 超时
    client_header_timeout 30s;
    client_body_timeout 30s;
    send_timeout 30s;
}

server {
    limit_req zone=req_limit burst=20 nodelay;
    limit_conn conn_limit 10;

    limit_req_status 429;
    limit_conn_status 429;
}

第二层:限制请求头大小和数量

http {
    # 限制请求头缓冲区(防止超大请求头攻击)
    client_header_buffer_size 1k;
    large_client_header_buffers 4 8k;

    # 限制请求体缓冲区
    client_body_buffer_size 8k;
}

第三层:内核参数调优

# /etc/sysctl.d/99-ddos.conf

# SYN Flood 防护
net.ipv4.tcp_syncookies = 1                # 开启 SYN Cookie
net.ipv4.tcp_max_syn_backlog = 65535        # SYN 队列长度
net.ipv4.tcp_synack_retries = 2             # SYN-ACK 重试次数(默认5,减少可防 SYN Flood)

# 连接复用
net.ipv4.tcp_tw_reuse = 1                   # 复用 TIME_WAIT
net.ipv4.tcp_fin_timeout = 15               # FIN-WAIT-2 超时

# 连接追踪
net.netfilter.nf_conntrack_max = 1000000    # 连接追踪表大小
net.netfilter.nf_conntrack_tcp_timeout_established = 1800  # 已建立连接超时

# ICMP 防护
net.ipv4.icmp_echo_ignore_broadcasts = 1    # 忽略广播 ICMP
net.ipv4.icmp_ignore_bogus_error_responses = 1

# 反向路径过滤
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# 应用配置
sudo sysctl -p /etc/sysctl.d/99-ddos.conf

9.3 集成 fail2ban 自动封禁

Nginx 限速能挡住超速请求,但恶意 IP 还是在不断尝试。fail2ban 可以监控 Nginx 日志,自动把超过阈值的 IP 用 iptables 封掉。如果不想手动维护 IP 黑名单,用 fail2ban 自动封禁是最省心的方案。
创建对应的 filter 文件,或使用 fail2ban 自带的 nginx-limit-req 过滤规则(如果存在)。

# 安装 fail2ban
sudo apt install fail2ban

# 创建 Nginx 限速 jail 配置
sudo tee /etc/fail2ban/jail.d/nginx-limit.conf << 'EOF'
[nginx-limit-req]
enabled = true
filter = nginx-limit-req
action = iptables-multiport[name=ReqLimit, port="http,https", protocol=tcp]
logpath = /var/log/nginx/error.log
findtime = 600
bantime = 7200
maxretry = 10

[nginx-botsearch]
enabled = true
filter = nginx-botsearch
action = iptables-multiport[name=BotSearch, port="http,https", protocol=tcp]
logpath = /var/log/nginx/access.log
findtime = 600
bantime = 7200
maxretry = 2
EOF

# 启动 fail2ban
sudo systemctl enable fail2ban
sudo systemctl restart fail2ban
参数
含义
推荐值
findtime
统计时间窗口(秒)
600(10 分钟)
maxretry
窗口内最大触发次数
10
bantime
封禁时长(秒)
7200(2 小时)
# 查看 fail2ban 状态
sudo fail2ban-client status nginx-limit-req

# 输出示例:
# Status for the jail: nginx-limit-req
# |- Filter
# |  |- Currently failed: 3
# |  |- Total failed:     156
# |  `- File list:        /var/log/nginx/error.log
# `- Actions
#    |- Currently banned: 5
#    |- Total banned:     23
#    `- Banned IP list:   1.2.3.4 5.6.7.8 9.10.11.12 13.14.15.16 17.18.19.20

# 手动解封某个 IP
sudo fail2ban-client set nginx-limit-req unbanip 1.2.3.4

💡 提示: fail2ban 和 Nginx 限速是互补关系:Nginx 限速挡住超速请求(返回 429),fail2ban 监控到某个 IP 频繁触发限速,直接用 iptables 封掉它的所有连接。两层防护,效果倍增。

十、SSL/TLS 安全加固

10.1 禁用不安全的协议和加密套件

server {
    listen 443 ssl http2;

    # 只允许 TLS 1.2 和 1.3
    ssl_protocols TLSv1.2 TLSv1.3;

    # 安全的加密套件
    ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;

    # 服务器优先选择加密套件
    ssl_prefer_server_ciphers on;

    # SSL 会话缓存
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_session_tickets off;

    # OCSP Stapling(让客户端不需要单独查询证书状态)
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 223.5.5.5 223.6.6.6 valid=300s;
    resolver_timeout 5s;
}
协议
是否安全
处理
SSLv2
❌ 不安全
禁用
SSLv3
❌ 不安全
禁用(POODLE)
TLSv1.0
❌ 不安全
禁用
TLSv1.1
❌ 不安全
禁用
TLSv1.2
✅ 安全
保留
TLSv1.3
✅ 安全
优先使用

10.2 HSTS 强制 HTTPS

server {
    # HSTS:告诉浏览器一年内只走 HTTPS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
}

⚠️ 注意: preload 参数意味着域名会被加入浏览器的 HSTS 预加载列表。一旦加入,所有用户访问域名都强制 HTTPS,无法回退到 HTTP。只有在确认所有子域名都支持 HTTPS 后才加 preload

十一、完整安全加固配置模板

把以上所有安全措施整合到一份配置里:

# /etc/nginx/nginx.conf

user www-data;
worker_processes auto;
worker_rlimit_nofile 65535;

pid /run/nginx.pid;
error_log /var/log/nginx/error.log warn;

events {
    worker_connections 4096;
    multi_accept on;
    use epoll;
}

http {
    # ===== 基础安全 =====
    server_tokens off;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 30s;

    # ===== 限制请求大小 =====
    client_header_buffer_size 1k;
    large_client_header_buffers 4 8k;
    client_body_buffer_size 8k;
    client_max_body_size 2m;

    # ===== 超时配置 =====
    client_header_timeout 30s;
    client_body_timeout 30s;
    send_timeout 30s;

    # ===== 限速 =====
    limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
    limit_req_status 429;

    # ===== 限连接 =====
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
    limit_conn_status 429;

    # ===== IP 封禁 =====
    geo $blocked_ip {
        default 0;
        # 在这里添加要封禁的 IP 段
        # 1.2.3.0/24    1;
        # 5.6.0.0/16    1;
    }

    # ===== Gzip =====
    gzip on;
    gzip_comp_level 5;
    gzip_min_length 1k;
    gzip_types text/plain text/css text/javascript application/javascript application/json application/xml image/svg+xml;
    gzip_vary on;

    # ===== SSL 全局 =====
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_session_tickets off;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    include /etc/nginx/conf.d/*.conf;
}
# /etc/nginx/conf.d/example.conf

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # ===== 安全响应头 =====
    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;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;" always;

    # ===== 限速 + 限连接 =====
    limit_req zone=req_limit burst=20 nodelay;
    limit_conn conn_limit 10;

    # ===== IP 封禁 =====
    if ($blocked_ip = 1) {
        return 403;
    }

    # ===== 只允许必要 HTTP 方法 =====
    if ($request_method !~ ^(GET|POST|HEAD|OPTIONS)$ ) {
        return 405;
    }

    # ===== 隐藏敏感文件 =====
    location ~ /\. {
        deny all;
        access_log off;
        log_not_found off;
    }

    location ~ ~$ {
        deny all;
        access_log off;
        log_not_found off;
    }

    location ~ \.(sql|bak|backup|old|swp|tmp|log|env)$ {
        deny all;
        access_log off;
        log_not_found off;
    }

    # ===== 后台管理——IP 白名单 =====
    location /admin {
        allow 192.168.0.0/16;
        allow 10.0.0.0/8;
        allow 123.45.67.89;
        deny all;
        proxy_pass http://127.0.0.1:3000;
    }

    # ===== 登录接口——更严格限速 =====
    location /api/login {
        limit_req zone=req_limit burst=3 nodelay;
        proxy_pass http://127.0.0.1:3000;
    }

    # ===== API =====
    location /api/ {
        limit_req zone=req_limit burst=10 nodelay;
        proxy_pass http://127.0.0.1:3000;
    }

    # ===== 图片防盗链 =====
    location ~ \.(jpg|jpeg|png|gif|webp|svg|mp4)$ {
        valid_referers none blocked server_names *.example.com;
        if ($invalid_referer) {
            return 403;
        }
        root /var/www/example;
        expires 30d;
    }

    # ===== 静态文件 =====
    location / {
        root /var/www/example;
        index index.html;
        try_files $uri $uri/ =404;
    }

    access_log /var/log/nginx/example.access.log;
    error_log  /var/log/nginx/example.error.log warn;
}

# HTTP → HTTPS 重定向
server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

十二、安全验证清单

上线前对照这张清单逐项检查:

检查项
命令
期望结果
版本号已隐藏
curl -I https://example.comServer: nginx
(无版本号)
安全头已添加
curl -I https://example.com
包含 X-Frame-Options 等
限速生效
ab -n 100 -c 20 https://example.com/
超出部分返回 429
.git 已封禁
curl -I https://example.com/.git/config
403 或 404
.env 已封禁
curl -I https://example.com/.env
403 或 404
TRACE 方法已禁止
curl -X TRACE https://example.com
405
HSTS 已启用
curl -I https://example.com
包含 Strict-Transport-Security
HTTPS 强制跳转
curl -I http://example.com
301 到 https
后台 IP 白名单
非白名单 IP 访问 /admin
403
fail2ban 运行
sudo fail2ban-client status
活跃 jail 列表

部署完成后用这些命令跑一遍,确认防护措施都生效了。

十三、常见问题排查

13.1 限速配置不生效

可能原因
检查方法
limit_req_zone 写在 server 块里
必须写在 http 块
limit_req 写在了 http 块
必须写在 server 或 location 块
没有 reload
sudo nginx -s reload
zone 名不匹配
检查 limit_req 的 zone 名和 zone 定义一致
burst 太大
burst 太大等于没限速
走了 CDN
CDN 回源时 IP 是 CDN 的,需要用 $http_x_forwarded_for

13.2 CDN 场景下限速失效

如果网站套了 CDN(如阿里云 CDN、腾讯云 CDN),Nginx 看到的 $remote_addr 是 CDN 节点 IP,不是用户真实 IP,限速会变成"对整个 CDN 节点限速"。

# 方案:用 real_ip 模块获取真实 IP

http {
    # 信任的 CDN IP 段(以阿里云为例)
    set_real_ip_from 47.0.0.0/8;
    set_real_ip_from 39.0.0.0/8;
    set_real_ip_from 120.0.0.0/8;

    # 从哪个头取真实 IP
    real_ip_header X-Forwarded-For;

    # 递归去除信任的代理 IP
    real_ip_recursive on;
}

⚠️ 注意: set_real_ip_from 必须只包含信任的 CDN/代理 IP。如果设成 0.0.0.0/0,任何人都可以伪造 X-Forwarded-For 头绕过限速。

13.3 allow/deny 不生效

可能原因
检查方法
规则顺序错误
allow/deny 从上到下匹配,先匹配先生效
走了反向代理
$remote_addr
 是代理 IP,需配 real_ip
location 优先级冲突
正则 location 优先级高于前缀 location

13.4 fail2ban 不封禁

可能原因
检查方法
日志路径不对
确认 logpath 和 Nginx 实际日志路径一致
filter 正则不匹配
sudo fail2ban-regex /var/log/nginx/error.log /etc/fail2ban/filter.d/nginx-limit-req.conf
findtime 太短
调大 findtime
maxretry 太大
调小 maxretry
iptables 规则被覆盖
sudo iptables -L -n
 检查 fail2ban 链

十四、速查表

限速速查

指令
作用
推荐值
limit_req_zone ... rate=
定义限速区域和频率
10r/s
limit_req zone= ... burst=
启用限速
burst=20
nodelay
突发请求不延迟
limit_req_status
限速响应码
429

封禁速查

方法
适用场景
示例
allow/deny
少量 IP 白名单/黑名单
deny 1.2.3.4;
geo
 模块
大量 IP 段分类
geo $blocked { ... }
fail2ban
自动封禁高频攻击 IP
监控日志 + iptables 封禁
iptables
系统级 IP 封禁
iptables -A INPUT -s 1.2.3.4 -j DROP

安全头速查

安全头
推荐值
X-Content-Type-Options
nosniff
X-Frame-Options
SAMEORIGIN
Strict-Transport-Security
max-age=31536000; includeSubDomains
Referrer-Policy
strict-origin-when-cross-origin
Content-Security-Policy
按需配置

超时速查

参数
推荐值
作用
client_header_timeout
30s
请求头超时
client_body_timeout
30s
请求体超时
send_timeout
30s
响应发送超时
keepalive_timeout
30s
空闲连接超时
client_max_body_size
2m
请求体最大大小

防护层级速查

防护层
工具/配置
防护内容
应用层 L7
Nginx limit_req
CC 攻击、API 滥用
应用层 L7
Nginx limit_conn
连接耗尽、慢速攻击
应用层 L7
fail2ban
自动封禁恶意 IP
传输层 L4
iptables
端口扫描、IP 封禁
内核层
sysctl
SYN Flood、ICMP
网络层
云服务商高防 IP / CDN
大流量 DDoS

小结

Nginx 安全加固的核心逻辑:

  1. 隐藏信息
    ——server_tokens off + 安全响应头,减少攻击者可获取的信息
  2. 限速防刷
    ——limit_req 控制请求频率,CC 攻击和 API 滥用的第一道防线
  3. 限连接防耗尽
    ——limit_conn 控制并发连接数,慢速攻击和连接耗尽的防线
  4. IP 封禁
    ——allow/deny 手动封禁,geo 模块批量分类,fail2ban 自动封禁
  5. 敏感路径保护
    ——封禁 .git.env、备份文件,防止源码和密码泄露
  6. SSL 加固
    ——禁用旧协议,只留 TLS 1.2/1.3,启用 HSTS

💡 提示: 安全加固不是一次性的工作。新的攻击手法不断出现,定期检查 Nginx 版本更新、审查日志中的异常请求、更新 fail2ban 规则,是保持安全的必要工作。

一个原则:多层防护,纵深防御。不要只靠一层——限速 + 限连接 + IP 封禁 + fail2ban + 内核参数 + 高防 IP,每一层都挡一部分攻击,叠加起来才能扛住真实流量。


阅读原文:点击这里


该文章在 2026/7/19 14:02:42 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved  粤ICP备13012886号-2  粤公网安备44030602007207号