将 docs/ 根目录下的 nginx 相关文档统一移动到 docs/nginx/ 子目录, 提高文档组织性和可维护性。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
735 lines
15 KiB
Markdown
735 lines
15 KiB
Markdown
# NGINX 安全与访问控制指南
|
||
|
||
## 1. 访问控制
|
||
|
||
### IP 访问控制
|
||
|
||
```nginx
|
||
# 允许/拒绝特定 IP
|
||
location /admin/ {
|
||
allow 192.168.1.0/24;
|
||
allow 10.0.0.1;
|
||
deny all;
|
||
}
|
||
|
||
# 仅允许本地访问内部接口
|
||
location /internal/ {
|
||
allow 127.0.0.1;
|
||
deny all;
|
||
}
|
||
```
|
||
|
||
### 地理位置访问控制(GeoIP)
|
||
|
||
```nginx
|
||
http {
|
||
geoip_country /usr/share/GeoIP/GeoIP.dat;
|
||
|
||
map $geoip_country_code $allowed_country {
|
||
default no;
|
||
CN yes;
|
||
US yes;
|
||
}
|
||
|
||
server {
|
||
if ($allowed_country = no) {
|
||
return 403;
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 外部认证 (ngx_http_auth_request_module)
|
||
|
||
NGINX 的 `ngx_http_auth_request_module` 模块(从 NGINX 1.5.4+ 开始支持,需编译时启用)提供外部认证功能,允许通过发送子请求到认证服务来决定是否允许访问。
|
||
|
||
### 2.1 指令说明
|
||
|
||
| 指令 | 语法 | 默认值 | 上下文 |
|
||
|------|------|--------|--------|
|
||
| `auth_request` | `auth_request uri \| off;` | `off` | http, server, location |
|
||
| `auth_request_set` | `auth_request_set $variable value;` | — | http, server, location |
|
||
|
||
### 2.2 工作原理
|
||
|
||
1. **NGINX 发送子请求**:当收到客户端请求时,NGINX 向指定的认证 URI 发送子请求
|
||
2. **认证服务响应**:认证服务返回 HTTP 状态码(200 表示通过,401/403 表示拒绝)
|
||
3. **结果判定**:
|
||
- 返回 200:NGINX 继续处理原请求
|
||
- 返回 401/403:NGINX 拒绝访问并返回相应状态码
|
||
- 其他状态码:视为错误,返回 500
|
||
|
||
**流程图**:
|
||
```
|
||
Client → NGINX → auth_request (子请求)
|
||
↓
|
||
认证服务
|
||
↓
|
||
200 OK? → 是 → 处理原请求 → 后端服务
|
||
↓
|
||
否
|
||
↓
|
||
返回 401/403 → Client
|
||
```
|
||
|
||
### 2.3 基础配置示例
|
||
|
||
```nginx
|
||
server {
|
||
listen 80;
|
||
server_name api.example.com;
|
||
|
||
location /protected/ {
|
||
auth_request /auth;
|
||
proxy_pass http://backend;
|
||
}
|
||
|
||
# 认证子请求 location
|
||
location = /auth {
|
||
internal; # 仅限内部子请求访问
|
||
proxy_pass http://auth-server/verify;
|
||
proxy_pass_request_body off; # 不传递请求体到认证服务
|
||
proxy_set_header Content-Length "";
|
||
proxy_set_header X-Original-URI $request_uri;
|
||
proxy_set_header X-Client-IP $remote_addr;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2.4 带变量提取的认证
|
||
|
||
从认证服务响应中提取自定义头部到变量:
|
||
|
||
```nginx
|
||
server {
|
||
location /api/ {
|
||
auth_request /auth;
|
||
auth_request_set $auth_user $upstream_http_x_user;
|
||
auth_request_set $auth_role $upstream_http_x_role;
|
||
|
||
proxy_pass http://backend;
|
||
proxy_set_header X-User $auth_user;
|
||
proxy_set_header X-Role $auth_role;
|
||
}
|
||
|
||
location = /auth {
|
||
internal;
|
||
proxy_pass http://auth-service/verify;
|
||
proxy_pass_request_body off;
|
||
proxy_set_header Authorization $http_authorization;
|
||
}
|
||
}
|
||
```
|
||
|
||
**可用变量**:
|
||
- `$upstream_http_*`:从认证服务响应中提取任意 HTTP 头部
|
||
- `$upstream_status`:认证服务返回的状态码
|
||
- `$upstream_response_time`:认证响应时间
|
||
|
||
### 2.5 JWT/OAuth2 集成示例
|
||
|
||
```nginx
|
||
server {
|
||
listen 443 ssl;
|
||
server_name api.example.com;
|
||
|
||
location /api/ {
|
||
auth_request /auth_jwt;
|
||
auth_request_set $jwt_claims $upstream_http_x_jwt_claims;
|
||
auth_request_set $jwt_sub $upstream_http_x_jwt_sub;
|
||
|
||
proxy_pass http://api_backend;
|
||
proxy_set_header X-JWT-Claims $jwt_claims;
|
||
proxy_set_header X-User-Id $jwt_sub;
|
||
|
||
# 自定义 401 响应
|
||
error_page 401 = @unauthorized;
|
||
}
|
||
|
||
location = /auth_jwt {
|
||
internal;
|
||
proxy_pass http://jwt-validator/verify;
|
||
proxy_set_header Authorization $http_authorization;
|
||
proxy_set_header X-Original-URI $request_uri;
|
||
proxy_pass_request_body off;
|
||
}
|
||
|
||
location @unauthorized {
|
||
default_type application/json;
|
||
return 401 '{"error":"Unauthorized","code":"INVALID_TOKEN"}';
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2.6 多级认证组合
|
||
|
||
结合 `auth_request` 与 `auth_basic`:
|
||
|
||
```nginx
|
||
server {
|
||
location /admin/ {
|
||
# 先进行基础认证
|
||
auth_basic "Admin Access";
|
||
auth_basic_user_file /etc/nginx/.htpasswd;
|
||
|
||
# 再进行外部权限校验
|
||
auth_request /auth_admin;
|
||
|
||
proxy_pass http://admin_backend;
|
||
}
|
||
|
||
location = /auth_admin {
|
||
internal;
|
||
proxy_pass http://permission-service/check-admin;
|
||
proxy_set_header X-User $remote_user;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2.7 错误处理
|
||
|
||
```nginx
|
||
server {
|
||
location /api/ {
|
||
auth_request /auth;
|
||
|
||
# 认证失败重定向到登录页
|
||
error_page 401 = @login;
|
||
|
||
proxy_pass http://backend;
|
||
}
|
||
|
||
location @login {
|
||
return 302 https://auth.example.com/login?redirect=$request_uri;
|
||
}
|
||
|
||
# 认证服务不可用时
|
||
error_page 500 = @auth_error;
|
||
|
||
location @auth_error {
|
||
return 503 '{"error":"Authentication service unavailable"}';
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2.8 应用场景
|
||
|
||
| 场景 | 实现方式 |
|
||
|------|----------|
|
||
| **OAuth2/OIDC 集成** | 认证服务验证 access_token,返回用户信息 |
|
||
| **JWT Token 验证** | 验证 JWT 签名和过期时间,提取 claims |
|
||
| **统一认证网关** | 集中处理多个服务的认证逻辑 |
|
||
| **权限分级验证** | 根据路径或资源进行细粒度权限检查 |
|
||
| **多因素认证** | 组合多种认证方式(密码 + 短信/邮件) |
|
||
| **API Key 验证** | 验证请求中的 API Key 有效性 |
|
||
|
||
### 2.9 最佳实践
|
||
|
||
**1. 认证服务高可用**
|
||
```nginx
|
||
upstream auth_backend {
|
||
server 192.168.1.10:8080;
|
||
server 192.168.1.11:8080 backup;
|
||
keepalive 32;
|
||
}
|
||
|
||
location = /auth {
|
||
internal;
|
||
proxy_pass http://auth_backend/verify;
|
||
proxy_connect_timeout 5s;
|
||
proxy_send_timeout 5s;
|
||
proxy_read_timeout 5s;
|
||
}
|
||
```
|
||
|
||
**2. 缓存认证结果**(减少重复验证)
|
||
```nginx
|
||
location /api/ {
|
||
auth_request /auth;
|
||
|
||
# 启用缓存(需配合 proxy_cache)
|
||
proxy_cache auth_cache;
|
||
proxy_cache_valid 200 1m;
|
||
|
||
proxy_pass http://backend;
|
||
}
|
||
```
|
||
|
||
**3. 调试认证流程**
|
||
```nginx
|
||
# 记录认证请求日志
|
||
log_format auth_log '$remote_addr - $time_local '
|
||
'auth_status=$auth_request_status '
|
||
'user=$auth_user';
|
||
|
||
access_log /var/log/nginx/auth.log auth_log;
|
||
```
|
||
|
||
---
|
||
|
||
## 3. HTTP 基础认证
|
||
|
||
### 配置认证
|
||
|
||
```nginx
|
||
location /admin/ {
|
||
auth_basic "Admin Area";
|
||
auth_basic_user_file /etc/nginx/.htpasswd;
|
||
}
|
||
```
|
||
|
||
### 创建密码文件
|
||
|
||
```bash
|
||
# 使用 htpasswd 创建
|
||
htpasswd -c /etc/nginx/.htpasswd user1
|
||
htpasswd /etc/nginx/.htpasswd user2
|
||
|
||
# 使用 openssl
|
||
echo "user1:$(openssl passwd -apr1 password1)" > /etc/nginx/.htpasswd
|
||
```
|
||
|
||
### 密码文件格式
|
||
|
||
```
|
||
# 注释
|
||
user1:encrypted_password1
|
||
user2:encrypted_password2:comment
|
||
```
|
||
|
||
**支持的密码类型**:
|
||
- `crypt()` 加密
|
||
- MD5 哈希(apr1)
|
||
- `{scheme}data` 语法(RFC 2307)
|
||
|
||
### 组合访问控制
|
||
|
||
```nginx
|
||
location /admin/ {
|
||
auth_basic "Admin Area";
|
||
auth_basic_user_file /etc/nginx/.htpasswd;
|
||
|
||
# 认证通过后,还需 IP 白名单
|
||
satisfy any; # any 或 all
|
||
|
||
allow 192.168.1.0/24;
|
||
deny all;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 请求限制
|
||
|
||
### 请求速率限制
|
||
|
||
```nginx
|
||
http {
|
||
# 定义限制区域
|
||
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
|
||
limit_req_zone $server_name zone=server_limit:10m rate=100r/s;
|
||
|
||
server {
|
||
location /api/ {
|
||
# 应用限制
|
||
limit_req zone=req_limit burst=20 nodelay;
|
||
limit_req zone=server_limit burst=50;
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**参数说明**:
|
||
| 参数 | 说明 |
|
||
|------|------|
|
||
| `zone=name:size` | 共享内存区域 |
|
||
| `rate=Nr/s` | 请求速率(每秒 N 个请求) |
|
||
| `burst=N` | 突发请求数量 |
|
||
| `nodelay` | 不延迟过量请求 |
|
||
| `delay=N` | 开始延迟的阈值 |
|
||
|
||
### 连接数限制
|
||
|
||
```nginx
|
||
http {
|
||
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
|
||
|
||
server {
|
||
location /download/ {
|
||
limit_conn conn_limit 10; # 每个 IP 最多 10 个连接
|
||
limit_conn_status 503; # 超限返回 503
|
||
limit_conn_log_level warn; # 日志级别
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 综合限制示例
|
||
|
||
```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;
|
||
|
||
server {
|
||
# 全局限制
|
||
limit_req zone=req_limit burst=20 nodelay;
|
||
limit_conn conn_limit 20;
|
||
|
||
location /api/ {
|
||
# API 接口更严格
|
||
limit_req zone=req_limit burst=5 nodelay;
|
||
limit_conn conn_limit 5;
|
||
}
|
||
|
||
location /static/ {
|
||
# 静态资源不限制
|
||
limit_req off;
|
||
limit_conn off;
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Dry Run 模式(限流测试)
|
||
|
||
NGINX 支持限流 dry run 模式,用于测试限流配置而不实际拒绝请求:
|
||
|
||
```nginx
|
||
server {
|
||
location /api/ {
|
||
limit_req zone=req_limit burst=20 nodelay;
|
||
limit_req_dry_run on; # 请求不被拒绝,但记录日志
|
||
|
||
limit_conn conn_limit 10;
|
||
limit_conn_dry_run on; # 连接不被拒绝,但记录日志
|
||
|
||
proxy_pass http://backend;
|
||
}
|
||
}
|
||
```
|
||
|
||
**Dry Run 作用**:
|
||
- 限流判断正常执行,但不返回 503/429 错误
|
||
- 记录 `limiting requests/connections, dry run` 到错误日志
|
||
- 用于评估限流阈值是否设置合理
|
||
|
||
> **注意**:详细限流配置请参考 [20-nginx-rate-limiting.md](./20-nginx-rate-limiting.md)
|
||
|
||
---
|
||
|
||
## 5. 安全头部
|
||
|
||
### 基础安全头部
|
||
|
||
```nginx
|
||
server {
|
||
# 隐藏版本号
|
||
server_tokens off;
|
||
|
||
# HSTS(强制 HTTPS)
|
||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
||
|
||
# 防止 MIME 类型嗅探
|
||
add_header X-Content-Type-Options "nosniff" always;
|
||
|
||
# XSS 保护
|
||
add_header X-XSS-Protection "1; mode=block" always;
|
||
|
||
# 点击劫持保护
|
||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||
|
||
# CSP
|
||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;
|
||
|
||
# Referrer 策略
|
||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||
|
||
# 权限策略
|
||
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
|
||
}
|
||
```
|
||
|
||
### X-Frame-Options 选项
|
||
|
||
| 值 | 说明 |
|
||
|-----|------|
|
||
| `DENY` | 完全禁止 iframe 嵌入 |
|
||
| `SAMEORIGIN` | 仅允许同源 iframe |
|
||
| `ALLOW-FROM uri` | 允许指定来源(已废弃) |
|
||
|
||
---
|
||
|
||
## 6. 防盗链
|
||
|
||
### 基础防盗链
|
||
|
||
```nginx
|
||
location ~* \.(jpg|jpeg|png|gif|webp|flv|mp4|swf)$ {
|
||
valid_referers none blocked server_names *.example.com example.* ~\.google\.;
|
||
|
||
if ($invalid_referer) {
|
||
return 403;
|
||
# 或返回替代图片
|
||
# rewrite ^/.*$ /hotlink.png break;
|
||
}
|
||
}
|
||
```
|
||
|
||
### valid_referers 参数
|
||
|
||
| 参数 | 说明 |
|
||
|------|------|
|
||
| `none` | 缺少 Referer 头 |
|
||
| `blocked` | Referer 被 firewall 等删除 |
|
||
| `server_names` | server_name 中的域名 |
|
||
| `*.domain` | 通配符域名 |
|
||
| `~regex` | 正则表达式 |
|
||
|
||
---
|
||
|
||
## 7. SSL/TLS 安全
|
||
|
||
### 协议配置
|
||
|
||
```nginx
|
||
server {
|
||
listen 443 ssl;
|
||
server_name example.com;
|
||
|
||
# 仅启用安全协议
|
||
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;
|
||
ssl_prefer_server_ciphers on;
|
||
|
||
# DH 参数
|
||
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
|
||
|
||
# 会话配置
|
||
ssl_session_cache shared:SSL:10m;
|
||
ssl_session_timeout 10m;
|
||
ssl_session_tickets off;
|
||
|
||
# OCSP Stapling
|
||
ssl_stapling on;
|
||
ssl_stapling_verify on;
|
||
}
|
||
```
|
||
|
||
### 生成 DH 参数
|
||
|
||
```bash
|
||
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 防止常见攻击
|
||
|
||
### SQL 注入防护
|
||
|
||
```nginx
|
||
# 阻止可疑请求
|
||
if ($request_uri ~* "(union|select|insert|drop|delete|update|cast|script|alert)") {
|
||
return 403;
|
||
}
|
||
|
||
# 或使用 ModSecurity 模块
|
||
```
|
||
|
||
### 防止目录遍历
|
||
|
||
```nginx
|
||
location ~* \.(git|svn|htpasswd|htaccess|env) {
|
||
deny all;
|
||
return 404;
|
||
}
|
||
|
||
# 禁止访问隐藏文件
|
||
location ~ /\. {
|
||
deny all;
|
||
return 404;
|
||
}
|
||
```
|
||
|
||
### 限制请求方法
|
||
|
||
```nginx
|
||
location / {
|
||
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
|
||
return 405;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 限制请求体大小
|
||
|
||
```nginx
|
||
http {
|
||
client_max_body_size 10m;
|
||
client_body_buffer_size 128k;
|
||
}
|
||
```
|
||
|
||
### 超时设置
|
||
|
||
```nginx
|
||
http {
|
||
client_body_timeout 10s;
|
||
client_header_timeout 10s;
|
||
send_timeout 10s;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 9. 限制特定 User-Agent
|
||
|
||
```nginx
|
||
# 阻止恶意爬虫
|
||
if ($http_user_agent ~* (bot|crawl|spider|scraper)) {
|
||
return 403;
|
||
}
|
||
|
||
# 阻止特定工具
|
||
if ($http_user_agent ~* (wget|curl|python-requests|scrapy)) {
|
||
return 403;
|
||
}
|
||
|
||
# 使用 map 更优雅
|
||
map $http_user_agent $block_ua {
|
||
default 0;
|
||
~*bot 1;
|
||
~*crawl 1;
|
||
~*spider 1;
|
||
}
|
||
|
||
if ($block_ua) {
|
||
return 403;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 10. WAF 配置(ModSecurity)
|
||
|
||
### 安装 ModSecurity
|
||
|
||
```bash
|
||
# Ubuntu/Debian
|
||
apt install libmodsecurity3 modsecurity-crs
|
||
|
||
# 加载模块
|
||
load_module modules/ngx_http_modsecurity_module.so;
|
||
```
|
||
|
||
### 配置示例
|
||
|
||
```nginx
|
||
modsecurity on;
|
||
modsecurity_rules_file /etc/nginx/modsecurity.conf;
|
||
|
||
# 使用 OWASP Core Rule Set
|
||
modsecurity_rules '
|
||
Include /usr/share/modsecurity-crs/*.conf
|
||
Include /usr/share/modsecurity-crs/rules/*.conf
|
||
';
|
||
```
|
||
|
||
---
|
||
|
||
## 11. fail2ban 集成
|
||
|
||
### 创建 filter
|
||
|
||
```ini
|
||
# /etc/fail2ban/filter.d/nginx-limit-req.conf
|
||
[Definition]
|
||
failregex = ^<HOST> -.*"(GET|POST|HEAD).*" (403|404|444|503).*$
|
||
ignoreregex =
|
||
```
|
||
|
||
### 创建 jail
|
||
|
||
```ini
|
||
# /etc/fail2ban/jail.local
|
||
[nginx-limit-req]
|
||
enabled = true
|
||
filter = nginx-limit-req
|
||
port = http,https
|
||
logpath = /var/log/nginx/*error.log
|
||
findtime = 60
|
||
bantime = 3600
|
||
maxretry = 10
|
||
```
|
||
|
||
---
|
||
|
||
## 12. 安全配置检查清单
|
||
|
||
### 基础安全
|
||
|
||
- [ ] 隐藏版本号 (`server_tokens off`)
|
||
- [ ] 禁用 SSLv2/SSLv3/TLSv1.0/TLSv1.1
|
||
- [ ] 配置 HSTS
|
||
- [ ] 设置安全头部
|
||
- [ ] 禁用自动索引 (`autoindex off`)
|
||
- [ ] 禁止访问隐藏文件
|
||
|
||
### 访问控制
|
||
|
||
- [ ] 管理后台 IP 白名单
|
||
- [ ] 内部接口认证保护
|
||
- [ ] 敏感目录访问限制
|
||
|
||
### 请求限制
|
||
|
||
- [ ] 请求速率限制
|
||
- [ ] 连接数限制
|
||
- [ ] 请求体大小限制
|
||
- [ ] 请求超时设置
|
||
|
||
### SSL/TLS
|
||
|
||
- [ ] 使用 TLSv1.2/TLSv1.3
|
||
- [ ] 配置安全加密套件
|
||
- [ ] 启用 OCSP Stapling
|
||
- [ ] 配置 DH 参数
|
||
- [ ] 禁用会话票据(或轮转密钥)
|
||
|
||
### 日志监控
|
||
|
||
- [ ] 记录访问日志
|
||
- [ ] 监控异常请求
|
||
- [ ] 配置 fail2ban
|
||
|
||
---
|
||
|
||
## 13. 安全测试工具
|
||
|
||
### 在线测试
|
||
|
||
- SSL Labs: https://www.ssllabs.com/ssltest/
|
||
- Security Headers: https://securityheaders.com/
|
||
- Observatory: https://observatory.mozilla.org/
|
||
|
||
### 命令行工具
|
||
|
||
```bash
|
||
# SSL 测试
|
||
testssl.sh example.com
|
||
|
||
# 头部检查
|
||
curl -I https://example.com
|
||
|
||
# 漏洞扫描
|
||
nikto -h https://example.com
|
||
nmap --script http-vuln* -p 443 example.com
|
||
``` |