Skip to content
bitzorcas
中EN

Guide

CORS、可信代理与安全响应头

从浏览器同源策略、可信代理拓扑和响应头三个边界配置并验证 API 入口安全。

Last updated

CORS、转发头和安全响应头解决的是三个不同问题:CORS 决定浏览器是否允许前端读取跨源响应;Forwarded Headers 决定应用是否相信代理报告的客户端 IP 与协议;安全响应头约束浏览器如何解释响应。它们都在 Host 组合根配置,业务模块不应各自复制一套策略。

请求进入 Host 的顺序

API 的关键顺序是 UseForwardedHeaders → ForwardedPortHostMiddleware → 异常处理与关联标识 → UseCors → Scalar CSP(文档面开启时)→ SecurityHeadersMiddleware → 认证。Forwarded Headers 必须在依赖 RemoteIpAddress 或 IsHttps 的组件之前运行;端口回写必须紧跟可信代理处理;CORS 必须位于认证之前。

SLB / CDN / OpenResty
│ X-Forwarded-For / X-Forwarded-Proto / X-Forwarded-Host / X-Forwarded-Port
▼
可信代理校验 → 公开端口回写 → CORS 预检 → 安全响应头 → Authentication → Tenant

Gateway 也执行可信代理解析和安全头注入。中间件采用“已有同名头则不覆盖”的规则,因此边缘层可以给出更严格策略,API 仍为受控直连提供纵深防御。

配置生产 CORS

生产环境应列出完整 Origin。AllowedMethods 和 AllowedHeaders 不配置时允许任意方法或头;PreflightMaxAgeHours 最小为一小时;AllowCredentials 只有显式开启才生效。

{
"Cors": {
"AllowedOrigins": [
"https://console.example.com",
"https://portal.example.com"
],
"AllowedMethods": [
"GET",
"QUERY",
"POST",
"PUT",
"PATCH",
"DELETE",
"OPTIONS"
],
"AllowedHeaders": [
"Authorization",
"Content-Type",
"X-Client-Platform",
"traceparent",
"correlationId",
"Idempotency-Key"
],
"AllowCredentials": true,
"PreflightMaxAgeHours": 1
}
}

Origin 包含协议、主机和端口,不包含路径。未配置任何 Origin 时策略默认拒绝跨域,但不会阻止 Host 启动。Development 在未显式配置 AllowAnyOrigin 时默认开放任意来源;生产不会自动开放。

QUERY 不在 CORS safelisted methods 中,分页、列表和复杂只读请求会先发 OPTIONS 预检。方法白名单漏掉 QUERY 时,@bitz/platform-sdk 会在浏览器报告网络错误后尝试 POST /_query;这能维持业务可用性,但会掩盖边缘层的配置缺口。生产验收仍须证明原生 QUERY 能通过真实 CDN、WAF、Ingress 与 Gateway。两种传输的差异见 QUERY 请求协议。

配置可信代理拓扑

API 处理 X-Forwarded-For、X-Forwarded-Proto 与 X-Forwarded-Host,紧随其后的端口中间件在可信条件成立时处理 X-Forwarded-Port。这些值共同决定 HTTPS、Cookie Origin 和 Scalar 调试 Server 使用的 Scheme/Host。ForwardedHeaders:ForwardLimit 控制采纳跳数:仓库 appsettings 默认常为 1;OpenResty → Gateway → API 两跳预览应设为 2。已知代理与网络会先清空再由配置重建,避免框架默认信任范围被意外保留。

{
"ForwardedHeaders": {
"ForwardLimit": 2,
"KnownProxies": ["10.20.0.10", "127.0.0.1"],
"KnownNetworks": ["10.30.0.0/16"],
"DirectExposure": false
}
}

无效 IP 或 CIDR 会在配置绑定时抛出异常。Production 与 Staging 还要求至少配置一个 KnownProxy/KnownNetwork,或明确声明 DirectExposure=true;没有拓扑声明时 Runtime Configuration Guard 拒绝启动。

直接相信公网传入的 X-Forwarded-* 会让攻击者伪造客户端地址、Host 或端口,进而影响 IP 限流、封禁、审计、登录 Cookie Origin 与文档调试地址。配置值必须描述“紧邻应用的可信上一跳”,而不是把全部互联网网段加入清单。浏览器 refresh-cookie 校验只使用 UseForwardedHeaders 与端口中间件校正后的 Scheme://Host,不会把未经验证的原始头当作可信 Origin。

OpenResty/Nginx 应使用 $http_host 填充 Host 与 X-Forwarded-Host,并单独发送 X-Forwarded-Port $server_port。$host 可能丢失 8088 一类非默认端口,使 Scalar 把调用发往 80/443。具体配置与回退语义见 OpenAPI 与 Scalar 文档面。

配置安全响应头

SecurityHeadersMiddleware 使用可热更新的 Security:Headers。默认开启 HSTS、nosniff、禁止 iframe、严格 referrer、同源 CSP,并禁用位置、麦克风和摄像头。

{
"Security": {
"Headers": {
"Enabled": true,
"EnableHsts": true,
"HstsMaxAgeSeconds": 63072000,
"HstsIncludeSubDomains": true,
"HstsPreload": false,
"ContentTypeOptions": "nosniff",
"FrameOptions": "DENY",
"ReferrerPolicy": "strict-origin-when-cross-origin",
"ContentSecurityPolicy": "default-src 'self'; script-src 'self'; connect-src 'self' https://api.example.com",
"PermissionsPolicy": "geolocation=(), microphone=(), camera=()"
}
}
}

HSTS 只有在请求经 Forwarded Headers 校正后被识别为 HTTPS、EnableHsts=true 且 max-age 大于零时才注入。启用 includeSubDomains 或 preload 前,必须证明所有子域都永久支持 HTTPS;预加载是浏览器厂商级承诺,不能只靠一次配置回滚撤销。

CSP 的落地方法

先以实际前端依赖建立最小清单,再按 script-src、style-src、img-src、font-src、connect-src 分类开放。默认策略为兼容 SPA 在 style 中保留 'unsafe-inline',这是一项兼容性取舍,不应被描述成最高强度基线。

生产变更应先在非阻断环境收集违规报告,再收紧策略。不要用 default-src *、长期开放 'unsafe-eval',也不要因为一个第三方组件报错就扩大所有资源类型。

Terminal window
# 验证实际响应头;使用真实入口域名才能覆盖 CDN/SLB 与 Gateway。
curl -sS -D - -o /dev/null https://api.example.com/health/live
# 模拟合法 Origin 的预检请求,核对 allow-origin/method/header。
curl -i -X OPTIONS https://api.example.com/api/tickets \
-H 'Origin: https://console.example.com' \
-H 'Access-Control-Request-Method: QUERY' \
-H 'Access-Control-Request-Headers: authorization,content-type,x-client-platform'

自动化契约测试

测试至少覆盖合法 Origin、未知 Origin、带凭据策略、HTTPS/HSTS 和已存在响应头不被覆盖。下面的断言骨架表达期望边界,测试夹具应使用仓库统一的 API factory。

// 使用未知 Origin 发出预检,服务可以响应,但不能授予该 Origin。
request.Headers.Add("Origin", "https://evil.example");
request.Headers.Add("Access-Control-Request-Method", "QUERY");
var response = await client.SendAsync(request);
// 拒绝证据是缺少授权响应头,不能只断言状态码。
response.Headers.TryGetValues("Access-Control-Allow-Origin", out _)
.ShouldBeFalse();

故障定位

浏览器提示 CORS 时先看 OPTIONS 是否到达正确 Host,再核对 Origin 字符串、方法和请求头。出现重定向循环或 HSTS 缺失时,检查可信代理是否正确回写 Request.Scheme。资源被 CSP 阻断则查看浏览器控制台中的具体 directive,和 CORS 分开定位。

常见误判是只用 Postman 验证 CORS、在 API 内信任任意转发头、或把 Gateway 写出的头当作 API 自身已配置。发布证据必须从公网入口和受控直连入口各采集一次。

发布验收

  • Production/Staging 的可信代理拓扑通过启动门禁;
  • 合法与非法 Origin 均有预检契约测试;
  • 公网入口确认 HSTS、CSP、frame、MIME、referrer 和 permissions 策略;
  • IP 限流与审计读取的是经可信代理校验后的地址;
  • CSP 例外有业务用途、负责人和移除条件;
  • Gateway 与 API 没有冲突或意外弱化的重复响应头。

相关主题

100%

滚轮或按钮缩放 · 放大后拖动画面 · 双击切换 100% / 200%