Files
wecom_it_smart_desk/docs/审计报告/CORS-CSP-安全Header全套.md
T
Simon 93ba41ed79 feat: 审批流程模块 (T审批A审批)
- 新增 backend/app/api/approval.py 审批API
- 前端H5支持发起审批、审批操作
- 添加审批卡片弹窗组件
- 路由注册审批模块
2026-06-15 09:32:41 +08:00

13 KiB

CORS / CSP / 安全 Header 全套审计与改进

审计日期: 2026-06-15 审计人: Claude(满载跑批) 关联: 风险跟踪表 / 后端架构 / 外部系统集成


📌 1. 现状盘点

1.1 后端 CORS 配置(backend/app/main.py:363)

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)

已有:

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:

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:

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:

# 新增
backend_env: str = "development"  # development / production

.env.production:

BACKEND_ENV=production
CORS_ORIGINS=https://itsupport.servyou.com.cn

2.3 CORS 验证脚本

# 验证 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(替换安全头部分)

# =================================================================
# 安全响应头配置(全部)
# =================================================================

# 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 适配)

# 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:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-Content-Type-Options" content="nosniff">
    
    <!-- CSP - 与 nginx 头保持一致 -->
    <meta http-equiv="Content-Security-Policy" content="
        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';
    ">
    
    <meta name="referrer" content="strict-origin-when-cross-origin">
    <meta http-equiv="Permissions-Policy" content="
        camera=(), microphone=(), geolocation=(), payment=()
    ">
    
    <title>IT 智能服务台 - 管理后台</title>
</head>

4.2 CSP 报告模式(先观察,再强制)

第 1 步: Report-Only 模式(2 周):

add_header Content-Security-Policy-Report-Only "..." always;

第 2 步: 收集违规报告(发到 /api/v1/csp-report)

第 3 步: 改 enforce 模式:

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 限制

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 不限 企微回调

配置示例:

# 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 在线检测

# 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(新建):

#!/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(新建):

#!/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 立即(本次跑批)

  • 审计报告写完(本文件)
  • 更新 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 季度


📌 9. 关联文档


本审计是 2026-06-15 Claude 满载跑批产出,待评审