关于跨域
什么是同源策略?
同源策略/SOP(Same origin policy)是一种约定,由 Netscape 公司 1995 年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到 XSS、CSFR 等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个 ip 地址,也非同源。
跨域通信:js 进行 DOM 操作、通信时如果目标与当前窗口不满足同源条件,浏览器为了安全会阻止跨域操作。
跨域通信的方法:
一. CORS 跨域
概念
CORS(Cross-Origin Resource Sharing 跨源资源共享),当一个请求 url 的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。
用法
- 在 a 页面,new 一个 XMLHttpRequest(),因考虑浏览器兼容问题,先判断浏览器是否为 ie,如下 function1;
- 然后调用 open 函数 初始化 HTTP 请求参数,但是并不发送请求。
open 语法
open(method, url, async, username, password)
// method为请求方式,值包括 GET、POST 和 HEAD;
// url 参数是请求的主体
// async 参数请求使用应该异步地执行,false就是等待有返回数据的时候再继续往下走,还没有得到数据的时候就会卡在那里,直到获取数据为止。如果这个参数是 false,请求是同步的,后续对 send() 的调用将阻塞,直到响应完全接收。如果这个参数是 true 或省略,请求是异步的,且通常需要一个 onreadystatechange 事件句柄。
// username 和 password 参数是可选的,为 url 所需的授权提供认证资格
// [详细](http://www.w3school.com.cn/xmldom/dom_http.asp)
;。)
true 就是不等待,直接返回,这就是所谓的异步获取数据!
<!--页面a (http://localhost:3000)-->
<button>通过CROS跨域</button>
<p>hello world 😘</p>
<script>
// function1
function createXmlHttpRequest(){
if(window.ActiveXObject){ //如果是IE浏览器
return new ActiveXObject("Microsoft.XMLHTTP");
}else if(window.XMLHttpRequest){ //非IE浏览器
return new XMLHttpRequest();
}
}
var btn = document.getElementsByTagName('button')[0];
var text = document.getElementsByTagName('p')[0];
btn.addEventListener('click', function () {
var xhr = createXmlHttpRequest();
xhr.open('GET', 'http://localhost:3001', true); // 与3001端口建立一个连接
xhr.send(null); // 发送给3001端口数据为空
xhr.onreadystatechange = () => { // 请求状态改变后调用这个函数
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { // 如果请求成功
text.innerHTML = xhr.response;// 处理数据
}
}
})
</script>
</body>
// 页面b (http://localhost:3001)
app.get("/", (req, res) => {
res.set("Access-Control-Allow-Origin", "http://localhost:3000"); // 设置允许跨域的origin,允许3000端口访问本端口(3001)
res.send("Hello world from CROS.");
});
安全隐患:
跨域请求和 Ajax 技术都会极大地提高页面的体验,但同时也会带来安全的隐患,其中最主要的隐患来自于 CSRF(Cross-site request forgery)跨站请求伪造。
CSRF 攻击的大致原理
- 用户通过浏览器,访问正常网站 A(例如某银行),通过用户的身份认证(比如用户名/密码)成功 A 网站。
- 网站 A 产生 Cookie 信息并返回给用户的浏览器;
- 用户保持 A 网站页面登录状态,在同一浏览器中,打开一个新的 TAB 页访问恶意网站 B;
- 网站 B 接收到用户请求后,返回一些攻击性代码,请求 A 网站的资源(例如转账请求);
- 浏览器执行恶意代码,在用户不知情的情况下携带 Cookie 信息,向网站 A 发出请求。
- 网站 A 根据用户的 Cookie 信息核实用户身份(此时用户在 A 网站是已登录状态),A 网站会处理该请求,导致来自网站 B 的恶意请求被执行。
二. JSONP 跨域
JSONP 跨域的原理
在同源策略下,在某个服务器下的页面是无法获取到该服务器以外的数据的,但 img、iframe、script 等标签是个例外,这些标签可以通过 src 属性请求到其他服务器上的数据。利用 script 标签的开放策略,我们可以实现跨域请求数据,当然,也需要服务端的配合。当我们正常地请求一个 JSON 数据的时候,服务端返回的是一串 JSON 类型的数据,而我们使用 JSONP 模式来请求数据的时候,服务端返回的是一段可执行的 JavaScript 代码。
<!--页面a (http://localhost:3000)-->
<body>
<p>hello worlda</p>
<script>
function myFunctionA(data) {
alert('获取数据成功,2s后改变数据!')
console.log(data);
let p = document.getElementsByTagName('p')[0]
setTimeout(function () {
p.innerHTML = data.message
}, 2000)
// 2s后p标签内的内容将改变
}
</script>
<script src="http://localhost:3001?callback=myFunctionA"></script>
<!--在src里面的问号?后面的参数({callback: 'myFunction'})可以在3001端口页面中可以通过req.query.callback获取-->
</body>
// 页面b (http://localhost:3001)
app.get("/", function(req, res) {
var callbackName = req.query.callback; // myFunctionA
res.send(callbackName + "({'message': 'hello world from JSONP!'});");
// myFunction({'message': 'hello world from JSONP!'})
// 一个带参数的执行函数
});
三. postMessage 跨域
用法:
- IFrame是从document取得的,即作作为document的子对象出现,虽然是文档(document)对象,但由于它是独立的页面,因而拥有自己的事件,拥有自己的窗口对象(contentWindow);
<!--页面a (http://localhost:3000)-->
<body>
<div id="father">
<p>这里3000端口</p>
<input type="text"/>
<button>发送信息</button>
<p style="text-align: left;">message : <span></span></p>
</div>
<iframe id="child" src="http://localhost:3001"></iframe>
<script>
var input = document.getElementsByTagName('input')[0];
var span = document.getElementsByTagName('span')[0];
var btn = document.getElementsByTagName('button')[0];
var frame = document.getElementById('child').contentWindow;
btn.onclick = function () {
var msg = input.value;
frame.postMessage('收到信息:' + msg + ' --from 3000 port!😨', 'http://localhost:3001');
}
function receiveMessage (event) {
if (event.origin !== 'http://localhost:3001') {
return false
}
var data = event.data;
span.innerHTML = data;
}
window.addEventListener('message', receiveMessage, false);
</script>
</body>
四. window. name 跨域
五. location.hash 跨域
六. document.domain 跨域
七. 后端设置代理 proxy 跨域
八. WebSocket 跨域
网友评论