同源策略与跨域技术详解
同源策略基础
同源策略(Same Origin Policy)是浏览器最基本也是最核心的安全功能,它限制了来自不同源的"document"或脚本对当前"document"读取或设置某些属性。
判断是否同源
影响源的因素包括:
- HOST:域名或IP地址
- 子域名
- 端口
- 协议
文件所在域不重要,重要的是文件解析加载所在的域。
同源判断示例:
| URL | 结果 | 原因 |
|---|---|---|
| http://blog.twosmi1e.com/dir2/test.html | success | 同源 |
| https://blog.twosmi1e.com/secure.html | failure | 不同协议 |
| http://blog.twosmi1e.com:90/dir2/etc.html | failure | 不同端口 |
| http://code.twosmi1e.com/dir/other.html | failure | 不同主机 |
跨域场景
常见的跨域业务场景包括:
- 前后端分离开发模式下,前后端域名不一致
- 本地开发时不同文件夹间的ajax请求
- 电商网站加载第三方快递物流信息
- 子站域名调用主站域名的用户资料接口
跨域解决方案
1. HTML标签跨域
<script>, ``, <iframe>, <link>等带src属性的标签都可以跨域加载资源,不受同源策略限制。
常见跨域标签:
<script src="..."></script>
<video src="..."></video>
<audio src="..."></audio>
<embed src="...">
<frame src="...">
<iframe src="..."></iframe>
<link rel="stylesheet" href="...">
<applet code="..."></applet>
<object data="..."></object>
CSS中@font-face也可以跨域:
@font-face {
src: url("http://example.com/font.ttf");
}
2. document.domain
同一主域不同子域之间可以通过设置document.domain为相同的高级域名来实现同源。
// 父域和子域都设置
document.domain = "example.com";
3. window.name
利用window.name在页面跳转后仍保留的特性实现跨域通信。
实现步骤:
- 在a.com/index.html中嵌入iframe,src设为b.com/index.html
- b.com/index.html设置window.name后跳转到a.com/empty.html
- a.com/index.html通过iframe.contentWindow.name获取数据
4. window.postMessage
HTML5引入的API,安全地实现跨域通信。
// 发送消息
targetWindow.postMessage(message, targetOrigin);
// 接收消息
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event) {
if (event.origin !== "http://example.com") return;
// 处理消息
}
5. location.hash
利用iframe的location.hash传值,通过中间页实现跨域通信。
6. Flash跨域
通过crossdomain.xml文件声明允许访问的域:
<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*.example.com" />
</cross-domain-policy>
7. JSONP
利用<script>标签无跨域限制的特性,通过回调函数获取数据。
实现流程:
- 定义回调函数
function handleResponse(data) {
console.log(data);
};
- 动态创建script标签
var script = document.createElement('script');
script.src = 'http://example.com/json?callback=handleResponse';
document.body.appendChild(script);
- 服务器返回
handleResponse({"name": "value"});
8. CORS (跨源资源共享)
现代浏览器支持的跨域解决方案,关键在于服务器配置。
简单请求
满足以下条件:
- 方法:GET、HEAD、POST
- 头信息限于:Accept、Accept-Language、Content-Language、Content-Type(值限于application/x-www-form-urlencoded、multipart/form-data、text/plain)
非简单请求
需要先发送OPTIONS预检请求。
HTTP头字段
Access-Control-Allow-Origin: 允许的域Access-Control-Allow-Credentials: 是否允许带凭证Access-Control-Expose-Headers: 允许访问的返回头Access-Control-Max-Age: 预检请求缓存时间Access-Control-Allow-Methods: 允许的方法Access-Control-Allow-Headers: 允许的自定义头
安全相关问题
CORS漏洞
漏洞原理:
服务器配置不当,Access-Control-Allow-Origin设置为*或直接取自请求头Origin字段,且Access-Control-Allow-Credentials设置为true。
攻击示例:
<script>
function cors() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
alert(this.responseText);
}
};
xhttp.open("GET", "https://target.com", true);
xhttp.withCredentials = true;
xhttp.send();
}
</script>
检测工具:
CORScanner
JSONP劫持
原理:
当网站使用JSONP方式跨域传递敏感信息且缺乏有效控制(对referer或token的检查)时,攻击者可构造恶意页面获取用户数据。
简单POC:
<script>
function jsonph(json){
alert(JSON.stringify(json))
}
</script>
<script src="https://target.com?callback=jsonph"></script>
SOME (Same Origin Method Execution)
不同于XSS盗取cookie,SOME直接劫持cookie进行操作,与CSRF类似但执行脚本代码。
防御建议
- 严格设置
Access-Control-Allow-Origin,避免使用* - 对JSONP接口进行referer检查或添加token验证
- 敏感操作使用CSRF Token防护
- 避免在客户端存储敏感信息
- 对跨域请求进行严格的输入验证和输出编码
总结
同源策略是Web安全的基础,理解其原理和各种跨域技术对于现代Web开发至关重要。在实际应用中,应根据具体场景选择适当的跨域方案,同时注意潜在的安全风险,做好相应的防护措施。