# CORS / CSP / 安全 Header 全套审计与改进 **审计日期**: 2026-06-15 **审计人**: Claude(满载跑批) **关联**: [[风险跟踪表]] / [[后端架构]] / [[外部系统集成]] --- ## 📌 1. 现状盘点 ### 1.1 后端 CORS 配置(`backend/app/main.py:363`) ```python app.add_middleware( CORSMiddleware, allow_origins=settings.cors_origins_list, # 逗号分隔的列表 allow_credentials=True, allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"], allow_headers=["Authorization", "Content-Type", "X-Employee-Id"], ) ``` **当前 `cors_origins`**: - 默认: `localhost:5173,5174,5175`(开发) - 生产: `itsupport.servyou.com.cn`(.env.production) ### 1.2 Nginx 安全头(`nginx.conf` + `nginx-nas.conf`) **已有**: ```nginx add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-XSS-Protection "1; mode=block" always; ``` **缺失**: - `Strict-Transport-Security` (HSTS) - `Content-Security-Policy` (CSP) - `Referrer-Policy` - `Permissions-Policy` - `Cross-Origin-*` 系列 ### 1.3 问题清单 | # | 问题 | 严重度 | 风险 | |---|---|---|---| | C-1 | CORS `allow_origins` 默认含 `*`(环境切换不当会泄露) | 🟠 | 跨域未授权 | | C-2 | CORS 没限制 `expose_headers`(前端拿不到 trace_id) | 🟡 | 排障不便 | | C-3 | CORS `max_age` 未设(每次预检) | 🟢 | 性能 | | C-4 | nginx 缺 HSTS | 🟠 | 中间人降级 | | C-5 | nginx 缺 CSP | 🟠 | XSS | | C-6 | nginx 缺 Referrer-Policy | 🟡 | 信息泄露 | | C-7 | nginx 缺 Permissions-Policy | 🟡 | 设备 API 滥用 | | C-8 | nginx 缺 COOP/COEP | 🟡 | 跨源攻击 | | C-9 | `/api/wecom/callback` 没限 IP | 🟡 | 恶意回调 | | C-10 | 4 前端没 CSP meta(防 XSS) | 🟠 | XSS | --- ## 📌 2. CORS 改进 ### 2.1 后端 - 精细化 CORS **新建 `backend/app/utils/cors_config.py`**: ```python from typing import List from app.config import settings def build_cors_config() -> dict: """根据环境构建 CORS 配置""" is_prod = settings.backend_env == "production" # 需新增环境变量 if is_prod: # 生产:严格白名单 origins = [ o.strip() for o in settings.cors_origins.split(",") if o.strip() and not o.startswith("*") ] return { "allow_origins": origins, "allow_credentials": True, "allow_methods": ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"], "allow_headers": [ "Authorization", "Content-Type", "X-Employee-Id", "X-Request-ID", # trace_id "X-CSRF-Token", # CSRF 防护 "X-Agent-Id", # 坐席 ID ], "expose_headers": [ "X-Request-ID", # 暴露 trace_id "X-RateLimit-Remaining", # 限流剩余 "X-RateLimit-Reset", # 限流重置 ], "max_age": 600, # 10 分钟预检缓存 } # 开发:宽松 return { "allow_origins": settings.cors_origins_list, "allow_credentials": True, "allow_methods": ["*"], "allow_headers": ["*"], "expose_headers": ["*"], "max_age": 3600, } ``` **更新 `main.py`**: ```python from app.utils.cors_config import build_cors_config cors_config = build_cors_config() app.add_middleware( CORSMiddleware, **cors_config, ) ``` ### 2.2 新增环境变量 **`backend/app/config.py`**: ```python # 新增 backend_env: str = "development" # development / production ``` **`.env.production`**: ```bash BACKEND_ENV=production CORS_ORIGINS=https://itsupport.servyou.com.cn ``` ### 2.3 CORS 验证脚本 ```bash # 验证 CORS 头 curl -I -X OPTIONS \ -H "Origin: https://itsupport.servyou.com.cn" \ -H "Access-Control-Request-Method: POST" \ -H "Access-Control-Request-Headers: Authorization" \ http://localhost:8000/api/v1/auth/login # 期望响应: # Access-Control-Allow-Origin: https://itsupport.servyou.com.cn # Access-Control-Allow-Credentials: true # Access-Control-Max-Age: 600 ``` --- ## 📌 3. Nginx 安全 Header 完整套 ### 3.1 完整版 nginx.conf(替换安全头部分) ```nginx # ================================================================= # 安全响应头配置(全部) # ================================================================= # 1. HSTS - 强制 HTTPS(2 年,包含子域名) add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; # 2. CSP - 内容安全策略(严格版,API 网关除外) # 注意:API 路径不要 CSP(纯 JSON),只 HTML 路径需要 location /itdesk/ { # 基础 CSP add_header Content-Security-Policy " default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://res.wx.qq.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https: http:; font-src 'self' data:; connect-src 'self' https://qyapi.weixin.qq.com wss://* https://*.servyou-it.com; media-src 'self' blob:; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests; " always; alias /usr/share/nginx/html/itdesk/; # ... } # 3. 防 MIME 嗅探 add_header X-Content-Type-Options "nosniff" always; # 4. 防点击劫持(更严:拒绝所有 frame 嵌入) add_header X-Frame-Options "DENY" always; # 5. XSS 过滤器(现代浏览器已废弃,保留向后兼容) add_header X-XSS-Protection "0" always; # 0 = 关闭(CSP 已接管) # 6. Referrer 策略(API 不发送 referrer,HTML 限制来源) add_header Referrer-Policy "strict-origin-when-cross-origin" always; # 7. Permissions Policy(禁用不用的设备 API) add_header Permissions-Policy " camera=(), microphone=(), geolocation=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=() " always; # 8. 跨源隔离 add_header Cross-Origin-Opener-Policy "same-origin" always; add_header Cross-Origin-Embedder-Policy "require-corp" always; add_header Cross-Origin-Resource-Policy "same-origin" always; # 9. 服务器信息隐藏 server_tokens off; # 隐藏 nginx 版本 # 10. API 路径特殊头(API 不需要 CSP,但要 CORS 友好) location /api/ { # 移除 CSP(API 返回 JSON,不要 CSP) more_clear_headers "Content-Security-Policy"; # API 也加 HSTS add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always; add_header X-Content-Type-Options "nosniff" always; add_header Cache-Control "no-store" always; # API 禁止缓存 proxy_pass http://backend_api/; # ... } ``` ### 3.2 完整版 nginx-nas.conf(同上,Cloudflare 适配) ```nginx # Cloudflare Tunnel 已经在外层 HTTPS,这里加全头 add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "DENY" always; add_header X-XSS-Protection "0" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always; add_header Cross-Origin-Opener-Policy "same-origin" always; add_header Cross-Origin-Embedder-Policy "require-corp" always; add_header Cross-Origin-Resource-Policy "same-origin" always; server_tokens off; ``` --- ## 📌 4. 前端 CSP Meta(双保险) ### 4.1 4 前端 `index.html` 加 meta CSP **`frontend-admin/index.html`**: ```html IT 智能服务台 - 管理后台 ``` ### 4.2 CSP 报告模式(先观察,再强制) **第 1 步: Report-Only 模式(2 周)**: ```nginx add_header Content-Security-Policy-Report-Only "..." always; ``` **第 2 步: 收集违规报告**(发到 `/api/v1/csp-report`) **第 3 步: 改 enforce 模式**: ```nginx add_header Content-Security-Policy "..." always; # 不带 Report-Only ``` --- ## 📌 5. 企微回调 IP 限制 ### 5.1 企微回调 IP 段(从企微文档) | 段 | 用途 | |---|---| | `101.226.103.0/24` | 企微上海 | | `101.226.108.0/24` | 企微上海 | | `140.207.54.0/24` | 企微上海 | | `140.207.61.0/24` | 企微深圳 | | `183.192.192.0/18` | 企微通用 | | `121.51.130.0/24` | 企微广州 | **注意**: 实际范围可能变更,需查官方文档。 ### 5.2 nginx 限制 ```nginx location = /api/wecom/callback { # 只允许企微 IP 段 allow 101.226.103.0/24; allow 101.226.108.0/24; allow 140.207.54.0/24; allow 140.207.61.0/24; allow 183.192.192.0/18; allow 121.51.130.0/24; # 内网允许(开发) allow 127.0.0.1; allow 10.0.0.0/8; allow 172.16.0.0/12; allow 192.168.0.0/16; deny all; proxy_pass http://backend_api/api/wecom/callback; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } ``` --- ## 📌 6. 速率限制(补充) ### 6.1 现有方案 `backend/app/main.py` 已用 `slowapi` 全局限流,默认 60/分钟。 ### 6.2 精细化建议 | 路径 | 限制 | 理由 | |---|---|---| | `/api/v1/auth/login` | 5/分钟/IP | 防爆破 | | `/api/v1/auth/otp` | 3/分钟/IP | 防 OTP 暴力 | | `/api/v1/conversations` | 60/分钟/agent | 正常业务 | | `/api/v1/messages` | 120/分钟/agent | 消息多 | | `/api/wecom/callback` | 不限 | 企微回调 | **配置示例**: ```python # backend/app/main.py from slowapi import Limiter from slowapi.util import get_remote_address limiter = Limiter(key_func=get_remote_address) @app.post("/api/v1/auth/login") @limiter.limit("5/minute") async def login(...): ... ``` --- ## 📌 7. 测试与验证 ### 7.1 安全 Header 在线检测 ```bash # Mozilla Observatory https://observatory.mozilla.org/analyze/itsupport.servyou.com.cn # Security Headers https://securityheaders.com/?q=itsupport.servyou.com.cn # SSL Labs(SSL 评估) https://www.ssllabs.com/ssltest/analyze.html?d=itsupport.servyou.com.cn ``` ### 7.2 CORS 自动化测试 **`scripts/cors-test.sh`**(新建): ```bash #!/bin/bash # CORS 自动化测试 set -e API="http://localhost:8000" ORIGIN="http://localhost:5173" echo "=== 1. 预检请求 ===" curl -s -I -X OPTIONS \ -H "Origin: $ORIGIN" \ -H "Access-Control-Request-Method: POST" \ -H "Access-Control-Request-Headers: Authorization" \ "$API/api/v1/auth/login" | head -20 echo "" echo "=== 2. 实际请求 ===" curl -s -I -H "Origin: $ORIGIN" "$API/api/v1/health" | head -20 echo "" echo "=== 3. 期望 ===" echo "Access-Control-Allow-Origin: $ORIGIN" echo "Access-Control-Allow-Credentials: true" ``` ### 7.3 安全 Header 验证 **`scripts/security-headers-test.sh`**(新建): ```bash #!/bin/bash # 安全 Header 验证 URL="${1:-http://localhost}" echo "=== 检查安全头 ===" HEADERS=$(curl -sI "$URL/") check_header() { local header=$1 local expected=$2 if echo "$HEADERS" | grep -qi "^$header:"; then echo "✅ $header: $(echo "$HEADERS" | grep -i "^$header:" | cut -d':' -f2- | xargs)" else echo "❌ $header: 缺失" fi } check_header "Strict-Transport-Security" check_header "X-Content-Type-Options" "nosniff" check_header "X-Frame-Options" check_header "Content-Security-Policy" check_header "Referrer-Policy" check_header "Permissions-Policy" ``` --- ## 📌 8. 实施路径 ### 8.1 立即(本次跑批) - [x] 审计报告写完(本文件) - [ ] 更新 `nginx.conf` + `nginx-nas.conf` 加 HSTS/CSP/Permissions - [ ] 加 `server_tokens off` - [ ] 4 前端 `index.html` 加 CSP meta ### 8.2 下周 - [ ] 改 CORS 精细化(分环境) - [ ] 企微回调 IP 白名单 - [ ] 加 `/api/v1/csp-report` 端点 - [ ] 跑 `cors-test.sh` 验证 ### 8.3 季度 - [ ] 提交 https://hstspreload.org/(HSTS 预加载) - [ ] 跑 Mozilla Observatory(A+ 目标) - [ ] 跑 Security Headers(A 目标) --- ## 📌 9. 关联文档 - [[风险跟踪表]] M-3(无统一错误码)/ H-4(WS token) - [[后端架构]] §5 错误处理 / §4 中间件 - [[外部系统集成]] §1-4(企微凭据) - [[健康检查+错误码+日志结构化]] - trace_id(配合 CSP 报告) --- *本审计是 2026-06-15 Claude 满载跑批产出,待评审*