前端攻防之解读浏览器同源策略
字数 2232 2025-08-07 08:22:20
浏览器同源策略全面解析与安全实践
0x0 前言
同源策略(Same Origin Policy, SOP)是浏览器安全的核心机制,但许多开发者对其理解仅停留在表面。本文将从定义、机制、实际案例到绕过方法,全面剖析同源策略及其安全影响。
0x1 同源策略定义
官方定义
- Firefox文档: 同源策略是一种网络浏览器的安全机制,防止不同网站相互攻击
- RFC 6454: 限制从一个源加载的文档/脚本如何与另一个源的资源交互
同源判定标准
两个URL同源需同时满足以下三个条件:
- 协议相同 (http/https/file等)
- 端口相同
- 主机名相同
示例分析:
| URL | 结果 | 原因 |
|---|---|---|
| http://store.company.com/dir2/other.html | 同源 | 仅路径不同 |
| https://store.company.com/secure.html | 不同源 | 协议不同 |
| http://store.company.com:81/dir/etc.html | 不同源 | 端口不同 |
| http://news.company.com/dir/other.html | 不同源 | 主机不同 |
0x2 同源策略的意义
没有同源策略的后果:
- 恶意网站可读取用户在其他网站(GMail、Facebook等)的敏感数据
- 用户浏览行为毫无隐私和安全保障
- 跨站脚本攻击(XSS)的危害将无限放大
0x3 同源策略机制
应用场景
浏览器在以下交互中实施来源检查:
-
DOM访问限制
- 页面无法访问不同源iframe的内容
- 限制跨域JavaScript对DOM的操作
-
数据存储隔离
- LocalStorage、IndexDB、Cookie按源隔离
- 禁止跨域读取敏感数据
-
网络请求控制
- 限制XMLHttpRequest跨域请求
- 规范fetch API的跨域行为
交互分类
- 跨域写操作:通常允许(如链接、重定向、表单提交)
- 跨域资源嵌入:通常允许(如脚本、图片、CSS、iframe)
- 跨域读操作:通常禁止
0x4 关键示例分析
4.1 document.domain误区
常见误区:
- 认为可以任意设置domain值为一级子域名
- 认为单方面设置document.domain即可跨域
正确理解:
- 只能设置为当前域或其上级域
- 必须双方同时设置相同document.domain
- 设置后端口会被置为null,需特别注意
实验代码:
<!-- xq17.com -->
<script>document.domain = 'xq17.com'; var flag= "flag:{xq17.com}";</script>
<!-- a.xq17.com -->
<script>
document.domain = 'xq17.com';
function load(){ console.log(frame.contentWindow.flag); }
</script>
<iframe src="http://xq17.com/" id="frame"></iframe>
4.2 跨域Cookie读取
Cookie共享规则:
- 不指定domain时,Cookie仅对当前域有效
- 指定domain为
.xq17.com时,所有子域可共享
Flask示例:
@app.route("/set_cookie")
def set_cookie():
resp = Response("success")
# 仅当前域有效
resp.set_cookie('flag', 'key pass', path='/')
# 所有子域共享
resp.set_cookie('flag', 'key pass', path='/', domain='.xq17.com')
return resp
4.3 window.name跨域技术
特性:
- 无视同源策略,窗口生命周期内所有页面共享
- 可用于跨域数据传递
利用方法:
<!-- b.xq17.com/data.html -->
<script>window.name = "敏感数据";</script>
<!-- a.xq17.com/getData.html -->
<script>
function getData(){
var iframe = document.getElementById('proxy');
iframe.src = 'a.xq17.com/a.html'; // 切换为同源
iframe.onload = function(){
console.log(iframe.contentWindow.name); // 获取数据
};
}
</script>
<iframe id="proxy" src="http://b.xq17.com/data.html" onload="getData()"></iframe>
4.4 安全利用思路
- 检查document.domain设置不当导致的跨域
- 寻找window.name中的敏感信息
- 探测共享Cookie配置不当的子域
- 利用postMessage实现跨域通信
0x5 主流跨域方法
5.1 CORS(跨源资源共享)
安全风险:
# 危险配置:允许任意源携带凭证
Access-Control-Allow-Origin $http_origin;
Access-Control-Allow-Credentials true;
Flask示例:
@app.route("/credential")
def get_credential():
resp = make_response("敏感数据")
origin = request.headers.get('Origin')
resp.headers['Access-Control-Allow-Origin'] = origin
resp.headers['Access-Control-Allow-Credentials'] = 'true'
return resp
SameSite影响:
- Lax模式(默认)下,跨站AJAX请求不会携带Cookie
- 缓解了CORS漏洞的危害
5.2 JSONP跨域
原理:
<script>
function exp(data){ console.log(data); }
</script>
<script src="http://b.xq17.com/jsonp?callback=exp"></script>
安全风险:
- 仅支持GET请求
- 回调函数注入导致XSS
防护措施:
- 设置Content-Type: application/javascript
- 对输出进行HTML编码
5.3 postMessage跨域
安全用法:
// 发送方
targetWindow.postMessage(message, 'https://trusted.com');
// 接收方
window.addEventListener('message', (event) => {
if(event.origin !== 'https://trusted.com') return;
console.log(event.data);
});
常见漏洞:
- 目标origin设置为
* - 接收方未验证event.origin
- 直接使用innerHTML导致XSS
0x6 SameSite机制
属性值对比
| 值 | 描述 | 适用场景 |
|---|---|---|
| Strict | 仅限第一方上下文 | 高敏感操作 |
| Lax(默认) | 允许顶级导航GET请求 | 平衡安全与可用性 |
| None | 允许所有跨站请求 | 必须配合Secure属性 |
浏览器兼容性
- Chrome 51+、Firefox 60+支持
- Chrome 80+默认Lax
- Firefox 96+默认Lax
eTLD+1规则
- 站(Site)定义为有效顶级域名(eTLD)+1
- 示例:
- a.web.dev和b.web.dev → 同站
- a.github.io和b.github.io → 跨站
0x7 总结
同源策略是Web安全的基石,但各种跨域技术带来了复杂的安全考量:
- CORS配置不当会导致敏感数据泄露
- JSONP可能引入XSS漏洞
- postMessage需严格验证origin
- SameSite机制有效缓解了CSRF和部分CORS风险
安全建议:
- 严格校验跨域请求的Origin头
- 避免使用不安全的跨域技术(document.domain)
- 关键Cookie设置SameSite=Strict
- 对所有用户输入进行编码和验证