name: cors-misconfiguration description: CORS 配置错误检测 — 检测跨域资源共享配置错误;适用于 Web API、前后端分离、网关代理场景。 when-to-use: 当任务涉及跨域访问控制、Origin 白名单、凭证跨域读取风险评估时 allowed-tools: bash,read_file,list_files,rg user-invocable: false
CORS 检测:跨域配置错误
成因引用
CORS 成因:source(攻击者控制的 Origin 头)→ sink(CORS 响应头:Access-Control-Allow-Origin / Access-Control-Allow-Credentials 等)。反射任意 Origin + Allow-Credentials=true,导致跨域读取受保护数据。详见同根目录 pentest/web-security-testing/SKILL.md 漏洞成因图谱 · CORS 配置错误行(不在本 skill 重复成因)。
关键 sink 形态:服务端在响应头里反射请求 Origin 作为 Access-Control-Allow-Origin 而不是用白名单——这是 CORS 配置错误的核心 sink,无论中间件实现方式如何(Go 反射 r.Header.Get("Origin") / Node CORS 通配 / Nginx 反向代理回填等)都属同 sink。
触发线索(基线检查项)
以下是已知的常见 CORS 触发线索,作为基线起点而非必检硬清单:
- 适用且已完成 → 标注
[x] done - 明确不适用 → 标注
[-] n/a (原因) - 基线未列出但实际发现 → 新增条目并标注
[+] added (来源)
基线触发线索按"sink 语义"分类(不按业务命名):
- 响应头含
Access-Control-Allow-Origin:任何返回 ACAO 头的端点都是候选——不限于"看起来像跨域"的 API - 反射 Origin 形态:响应的
Access-Control-Allow-Origin: <reflected from request Origin>(非固定白名单值) Access-Control-Allow-Credentials: true:与上面任一组合都是高危——攻击者可携带凭证跨域读nullOrigin 信任:服务端把Origin: null也作为合法值反射- 宽泛后缀 / 前缀匹配:如
evil.target.com被信任(target.com 后缀匹配缺陷) - 预检过宽:
OPTIONS预检返回Access-Control-Allow-Methods/Headers包含敏感方法 / 自定义头 - 跨子系统覆盖:多个子系统都有独立 CORS 中间件——每个子系统的 CORS 配置都需独立测
- 代码模式:后端代码出现
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))+w.Header().Set("Access-Control-Allow-Credentials", "true")形态
思考检查点
加载本 skill 时按这些问题思考:
- 这个端点返回的 ACAO 是固定白名单还是反射 Origin?
- 是否同时设置了
Allow-Credentials: true?(两者组合即高危) - 如果反射 Origin,是否也信任
null? - 子系统级 vs 端点级 CORS:多子系统场景下,每个子系统的 CORS 中间件是不是都需独立测?
- 端点返回的响应是否含敏感数据?(CORS 配置错误本身需结合"可读敏感数据"才判 confirmed)
方法论
- 基线请求(必做):不带
Origin与带合法Origin各发起一次,记录 ACAO/ACAC/Vary - 恶意 Origin 变异:使用
https://evil.example、null、相似子域、随机端口等 Origin 重放请求 - 凭证场景验证:对需要登录的接口携带 Cookie/Authorization,观察是否允许跨域读敏感响应
- 预检验证(按需):发送
OPTIONS预检,检查Access-Control-Allow-Methods/Headers是否过宽 - 复核:采用足以排除缓存、网关改写与偶发头部波动的方式,确认跨域读取行为真实存在
探测 payload 集
| Origin payload | 用途 |
|---|---|
https://evil.example |
标准恶意 Origin |
null |
检测 null Origin 信任(如 sandboxed iframe) |
https://evil.example:9999 |
端口变体,检查白名单是否含端口 |
https://target.com.evil.example |
后缀匹配绕过 |
https://evil-target.com |
前缀匹配绕过 |
http://evil.example(HTTP 而非 HTTPS) |
协议混合 |
示例库
正例形态(代码层根因)
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))+w.Header().Set("Access-Control-Allow-Credentials", "true")— Go 中间件直接反射 Origin(origin-reflected-with-credentials-cors)cors({ origin: true, credentials: true })— Express CORS 配置origin: true反射所有 Origin(expressjs-origin-true-cors)w.Header().Set("Access-Control-Allow-Origin", "*")但端点返回敏感数据 — 通配符 + 敏感数据(wildcard-acao-sensitive-cors)
窄化反例(必须避免)
以下是 CORS 维度的典型窄化误判:
- "已在某子系统命中 CORS 反射 → 其他子系统也假定有同问题" — 错误的另一面。CORS 是按子系统 / 按服务独立配置的,每个子系统的 CORS 中间件可能完全不同。必须对每个子系统独立测,但不能仅凭一个命中就推断其他全有(也不能反之,仅凭一个安全就推断其他全安全)
- "已测了 GET 请求 → 跳过 OPTIONS 预检" — 错。预检可能暴露
Access-Control-Allow-Methods/Headers配置过宽问题,必须独立测 - "
Access-Control-Allow-Origin: *不算高危" — 不完全错,但当响应含敏感数据 + 需要凭证时仍属高危。需结合端点的数据敏感度判定 - "参数名 / 路径不含 'api' → 跳过 CORS" — 错。任何返回 ACAO 头的端点都是候选,不限于显式 API 路径
- "已测 https://evil.example → 跳过 null Origin" — 错。
nullOrigin(来自 sandboxed iframe / file:// 等场景)是独立攻击面,需独立测
反例义务(必须遵守)
为什么这里是「必须」:反例义务属于交付契约——"该子系统 CORS 配置安全"或"已防护"结论是覆盖完整性的产物声明,缺失反向验证清单会让下游误信"该维度全站安全"。
写"未发现 CORS 配置错误"或"已防护"前,产物必须包含:
- 测过的 CORS 候选端点完整清单(按 sink 语义枚举:所有返回 ACAO 头的端点;多子系统场景按子系统独立分组结账)
- 每个端点测过的 Origin payload 类型(恶意 Origin / null / 子域 / 端口变体 / 协议变体)
- 每个端点的响应证据(ACAO / ACAC / Vary 头是否反射 / 通配 / 白名单)
- 凭证场景测试结果(带 Cookie/Authorization 是否仍允许跨域读)
清单不完整 → 结论降级为 partial-coverage 并显式声明未覆盖范围。
特别警示:多子系统场景下,CORS 配置按子系统独立结账——不能用"在子系统 A 测了"代替"子系统 B/C/D 也安全"的结论。
闭环验证要求(必须遵守)
通用闭环口径见同根目录 common/closure-verification.md(技能表 path 列同一抽取根下,需要时 read_file 读取)。核心:结论须形成「输入 → 处理 → 真实危害 → 可复核证据」完整证据链;仅凭响应头出现 Access-Control-Allow-Origin 等中间信号最多判 suspected,证明攻击源可携带凭证跨域读到敏感响应才判 confirmed。
实际效果验证方向(至少证明一类)
- 恶意 Origin 在真实浏览器跨域场景下能读取受保护接口返回的敏感数据
- 凭证场景下,浏览器会携带 Cookie/Authorization,且响应对恶意站点可读
- 若只有 ACAO/ACAC 头部异常,但未证明浏览器端真实可读性或敏感数据暴露,不能给
confirmed
判定标准
| 现象 | 判定 |
|---|---|
| 恶意 Origin 被允许,且在真实浏览器跨域场景下可读取敏感响应(含凭证场景) | confirmed |
配置看似宽松(如反射 / null),但尚未证明真实浏览器可读性或敏感数据暴露 |
suspected |
| 仅允许受控白名单 Origin,且敏感接口无跨域读风险 | not vulnerable |
| 只测了部分子系统 / 端点 / Origin payload 类型 | partial-coverage(不得宣称 safe) |
关键检测要点
- 必须结合"是否可读取敏感数据"判定风险,不能只看头字段存在
ACAO: *常见于公开静态资源;若无敏感数据与凭证,不应直接判高危- 注意
Vary: Origin、缓存层与网关改写,避免一次性结果误判
修复建议
- 使用严格 Origin 白名单(精确匹配协议+域名+端口)
- 禁止在敏感接口对任意 Origin 返回可读权限
- 仅在确有需要时启用
Access-Control-Allow-Credentials: true - Go 场景:避免
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))直接反射;改为白名单校验后输出