12 防范测试正常、上线失败

1. 为什么安全 Header 特别容易出现环境差异

开发环境通常是:

http://localhost:5173 -> http://localhost:8080

生产环境可能增加:

因此“本地 Fetch 成功”只覆盖很小一部分链路。

2. 不要在生产一次性打开所有严格策略

尤其是 CSP,应先使用 Report-Only:

Content-Security-Policy-Report-Only: default-src 'self'; connect-src 'self' https://api.example.com

它记录违规但暂不阻止资源。收集真实页面依赖后,再逐步切换为强制 CSP。报告也可能包含噪声或被扩展污染,必须结合页面回归判断。

CORP、COEP、COOP 也要围绕图片、下载、iframe、OAuth 弹窗和第三方资源逐项验证,而不是看到 Helmet 推荐就全部开启最严格值。

3. 使用环境变量维护 Origin 白名单

示例环境值:

CORS_ALLOWED_ORIGINS=https://www.example.com,https://admin.example.com

服务启动时应:

  1. 按逗号拆分并去除空白;
  2. 使用 new URL() 验证;
  3. 只保留 url.origin,拒绝带用户名密码等异常配置;
  4. 生产环境若列表为空则启动失败,而不是自动回退到 *
  5. 日志记录启用的来源数量和环境,不记录敏感凭据。

4. 建立上线前响应矩阵

对每个生产入口验证:

维度 至少覆盖
Origin 允许、拒绝、无 Origin
方法 GET、JSON POST、PATCH/DELETE
响应 200、业务失败、401、403、404、500
预检 OPTIONS + Authorization/Content-Type
内容 JSON、下载、图片、HTML 预览
链路 直连 Express、经 Nginx、公网/CDN

不要只检查 Header 是否“存在”,还要检查值是否唯一、是否与请求 Origin 匹配。

5. 自动化冒烟检查

正式请求:

curl -isk https://api.example.com/health \
  -H "Origin: https://www.example.com"

预检:

curl -isk -X OPTIONS https://api.example.com/api/users \
  -H "Origin: https://www.example.com" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: authorization,content-type"

拒绝来源:

curl -isk https://api.example.com/api/users \
  -H "Origin: https://attacker.example"

-i 显示响应头,-s 关闭进度信息,-k 跳过证书校验。生产证书验收时不应使用 -k,否则会掩盖证书链和域名错误。

curl 不执行浏览器 CORS/CSP 策略,所以它适合验证服务端 Header,但最终必须用真实浏览器测试 Console 和 Network。

6. 高频生产故障

实际 Origin 与配置不同

https://example.comhttps://www.example.comhttp://example.com 是不同 Origin。重定向前后的响应也可能缺少 CORS Header。

Cookie 本地正常,线上未发送

检查:

预检被认证或代理拦截

检查 OPTIONS 是否到达 Express、是否被 Nginx返回 404/405、是否先经过 JWT 中间件。

CSP 造成生产白屏

查看 Console 里的具体 directive,而不是直接关闭整个 CSP。常见遗漏有 API connect-src、CDN script-src、字体 font-src 和图片 img-src

缓存串用 Origin

检查 Vary: Origin、CDN cache key、Nginx 缓存,以及策略更新后旧预检的 Max-Age

重复 Header

公网响应出现两个 Access-Control-Allow-Origin 时,对比直连 Express 与经过 Nginx 的响应,确定唯一配置层。

7. 发布与回滚建议

8. 最终检查清单