跨域了解多少
同源策略
浏览器最核心最基本的安全策略,若缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击
- 同源:协议+ 域名+ 端口,相同
- 同源策略限制:
- cookie、localStorage、IndexDB
- DOM、JS对象无法获取
- ajax请求不能发送
跨域不一定是浏览器限制了发起跨站请求,也可能是跨站请求可以正常发起,但是返回结果被浏览器拦截了。最好的例子是 CSRF 跨站攻击原理,请求是发送到了后端服务器无论是否跨域!注意:有些浏览器不允许从 HTTPS 的域跨域访问 HTTP,比如 Chrome 和 Firefox,这些浏览器在请求还未发出的时候就会拦截请求,这是一个特例。
实现跨域
- jsonp
- cors(跨域资源共享)
- postMessage + iframe
- window.name + iframe
- location.hash + iframe
- document.domain + iframe
- websocket协议
- http-proxy(nodejs中间件)
- nginx
利用iframe的onload事件
jsonp
利用src引入外域资源不受限制,通过创建动态script,使src指向一个跨域url,设定数据和回调函数。
回调函数是前后台约定的查询参数,后台返回一个可执行的JS文件即回调函数,这样浏览器就会调用 callback 函数,并传递解析后 json 对象作为参数。
1 | function jsonp(options) { |
- 优点:兼容性强、简单易用、能之间访问文本,支持浏览器与服务器之间双向通信
- 缺点:
- 只能get请求,这是由于 script 标签自身的限制决定的。
- 需要保证请求的目的地的绝对安全
- jsonp 由于不是通过 XMLHttpRequest 进行传输,所以不能注册 success 和 error 等事件监听函数。无法判断请求是否失败,没有错误处理
CORS 跨域资源共享
跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
允许浏览器向跨域服务器发出XMLHttpReuest请求,克服ajax只能同源的限制(关键在于服务器)
请求类型
- 请求方式是head、get、post这3种
- http的头信息不能超出一下几种字段
1 | accept、accept-languge、content-languge、lase-event-id |
浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。
服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源
值得注意的是 CORS 请求中必定包含 Origin 头部,但是包含此头部不一定意味着这个请求就是 CORS 请求。
所有CORS相关的头部均以Access-Control-开头
- Access-Control-Allow-Origin(required):此头部必须添加到响应报文中,不然缺省值会导致 CORS 请求失败。你可以设置 * 值让所有站点都可以访问你的数据,但最好还是控制一下
- Access-Control-Allow-Credentials(optional):设置此头部的值为 true,如果你想要请求附带 cookies。与上文提到的 withCredentials 属性协作。若此头部值为 true 而 withCredentials 属性为 false,会导致请求失败,反之亦然
- Access-Control-Allow-Expose-Headers(optional):XMLHttpRequest2 对象存在 getResponseHeader 方法,允许访问一些简单的响应头部如:Content-Type,Cache-Control 等等。如果想暴露一些特殊的头部,可以在此头部的值设置以逗号分隔的头部名称
1 | // 服务器 |
预检请求
“需预检的请求”要求必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。”预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
postMessage + iframe(2个页面)
postMesaage是HTML5新定义的通信机制,该api定义在window对象
- 用于解决:
- 页面和其打开新窗口的数据传递
- 多窗口之间消息传递
- 页面与嵌套的iframe消息传递
这种方式通常用于获取嵌入页面中的第三方页面数据。一个页面发送消息,另一个页面判断来源并接收消息
原理:使用postMessage方法必须有其他窗口的引用otherWindow发送方:
1 | otherWindow.postMessage(data, targetOrigin, [transfer]) |
接收方通过监听message事件获取数据
1 | window.addEventListner('message',(e)=>{ |
实例
1 | // a页面 |
window.name + iframe(3个页面)
name值在不同页面(设置不同域名)记载后依旧存在,且支持非常长的值(2MB)
- 原理:通过iframe的跨域能力,首先将src指向请求的地址,在请求的页面设置window.name,若不同源的话,则在加载好请求后将src由外域指向本地域(同源的html文件/ ‘about:blank;’),然后就可获取window.name
实例
- a、b同域,a获取c的数据
- a先引用c,c设置window.name,加载好后将a的引用地址改为同源的b
1 | // a页面 |
location.hash + iframe(3个页面)
url路径后面的hash值可以用来通信,不同域的利用iframe的location.hash传值,相同域之间直接JS访问来通信(window.parent)
- a、c不同域,a、b同域,当a访问c的时候
- a给c传一个hash值,c收到hash后把hash直接传递给b
- b再将hash放到a的hash中,最后a通过监听hashchange事件获取数据
实例
1 | // a页面 |
document.domain
- 仅限主域相同,子域不同的跨域
- 两个页面都通过JS设置document.domain为主域实现同域,子窗口通过window.parent.xx获取父窗口的变量
1 | // a嵌套iframe src为b |
websocket
WebSocket protocol 是 HTML5 一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是 server push 技术的一种很好的实现。
1 | // 前端 |
nginx
同源策略是浏览器的安全策略,不是http协议的一部分,服务器端调用http接口只是使用http协议,不会执行js脚本,不需同源策略,也不会存在跨域问题
- 通过nginx配置一个代理服务器(域名与domain1相同,端口不同)
- 反向代理访问domain2接口
jsonp的安全性问题
1. jsonp劫持(属于csrf攻击)
当某个网站通过jsonp的方式跨域传递用户认证后的敏感信息时,攻击者可以构造恶意的JSONP调用页面诱导用户访问,已达到截取用户敏感信息的目的。解决:限制refer、部署token
2. callback中植入脚本的漏洞
解决:在响应头加上content-type:application/json:charset=utf-8