详解浏览器跨域的几种方法

48次阅读

共计 7787 个字符,预计需要花费 20 分钟才能阅读完成。

摘要:本文针对浏览器的跨域个性,做一下深刻介绍,以便咱们在进行 WEB 前端开发和测试时,对浏览器跨域个性有全面的了解和把握。

1 前言

在 WEB 前端开发中,咱们常常会碰到“跨域”问题,最常见的就是浏览器在 A 域名页面发送 B 域名的申请时会被限度。跨域问题波及到 WEB 网页安全性问题,使用不当会造成用户隐衷泄露危险,但有时业务上又须要进行跨域申请。如何正确的应用跨域性能,既能满足业务需要,又可能满足安全性要求,显得尤为重要。

本文针对浏览器的跨域个性,做一下深刻介绍,以便咱们在进行 WEB 前端开发和测试时,对浏览器跨域个性有全面的了解和把握。

2 背景常识介绍

2.1 同源政策

1995 年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都履行这个政策。

最后,它的含意是指,A 网页设置的 Cookie,B 网页不能关上,除非这两个网页“同源”。所谓“同源”指的是“三个雷同”:

  • 协定雷同
  • 域名雷同
  • 端口雷同

同源政策的目标,是为了保障用户信息的平安,避免歹意的网站窃取数据。

构想这样一种状况:

A 网站是一家银行,用户登录当前,A 网站在用户的机器上设置了一个 Cookie,蕴含了一些隐衷信息(比方贷款总额)。用户来到 A 网站当前,又去拜访 B 网站,如果没有同源限度,B 网站能够读取 A 网站的 Cookie,那么隐衷信息就会透露。更可怕的是,Cookie 往往用来保留用户的登录状态,如果用户没有退出登录,其余网站就能够假冒用户,随心所欲。因为浏览器同时还规定,提交表单不受同源政策的限度。由此可见,同源政策是必须的,否则 Cookie 能够共享,互联网就毫无平安可言了。

以后,如果非同源,共有三种行为受到限制:

  • Cookie、LocalStorage 和 IndexDB 无奈读取
  • DOM 无奈取得
  • AJAX 申请不能发送

2.2 为什么要有跨域限度

Ajax 的同源策略 次要是为了避免 CSRF(跨站申请伪造) 攻打,如果没有 AJAX 同源策略,相当危险,咱们发动的每一次 HTTP 申请都会带上申请地址对应的 cookie,那么能够做如下攻打:

  • 用户登录了本人的银行页面 mybank.com,mybank.com 向用户的 cookie 中增加用户标识。
  • 用户浏览了歹意页面 evil.com。执行了页面中的歹意 AJAX 申请代码。
  • evil.com 向 http://mybank.com 发动 AJAX HTTP 申请,申请会默认把 http://mybank.com 对应 cookie 也 …。
  • 银行页面从发送的 cookie 中提取用户标识,验证用户无误,response 中返回申请数据。此时数据就泄露了。
  • 而且因为 Ajax 在后盾执行,用户无奈感知这一过程。

DOM 同源策略 也一样,如果 iframe 之间能够跨域拜访,能够这样攻打:

  • 做一个假网站,外面用 iframe 嵌套一个银行网站 mybank.com。
  • 把 iframe 宽高啥的调整到页面全副,这样用户进来除了域名,别的局部和银行的网站没有任何差异。
  • 这时如果用户输出账号密码,咱们的主网站能够跨域拜访到 http://mybank.com 的 dom 节点,就能够拿到用户的输出了,那么就实现了一次攻打。

所以有了跨域拜访限度之后,咱们才可能平安的上网。

3 浏览器跨域的解决方案

3.1 CORS 规范

CORS 是一个 W3C 规范,全称是跨域资源共享(CORSs-origin resource sharing),它容许浏览器向跨源服务器,收回 XMLHttpRequest 申请。

其实,精确的来说,跨域机制是阻止了数据的跨域获取,不是阻止申请发送。

CORS 须要浏览器和服务器同时反对。目前,所有浏览器都反对该性能,IE 浏览器不能低于 IE10。

https://caniuse.com/#search=cors

整个 CORS 通信过程,都是浏览器主动实现,不须要用户参加。对于开发者来说,CORS 通信与同源的 AJAX 通信没有差异,代码齐全一样。浏览器一旦发现 AJAX 申请跨源,就会主动增加一些附加的头信息,有时还会多出一次附加的申请,但用户不会有感觉。

因而,实现 CORS 通信的要害是服务器。只有服务器实现了 CORS 接口,就能够跨域通信。

3.2 CORS 跨域断定的总体流程

如图所示,跨域的断定流程为:

  • 网页上的 JS 代码,从浏览器上发送 XMLHttpRequest 申请到服务端
  • 如果该申请为 简略申请,浏览器会间接发送理论申请到服务端,浏览器会依据服务端的响应,判断该申请是否能够跨域:

    (1)如果不能跨域,浏览器会报错,阻止 JS 代码进一步执行;

    (2)如果可能跨域,则 JS 能失常解决响应,进行后续业务流程

  • 如果该申请为 非简略申请,浏览器会先发送一个预检申请(preflight),办法为 OPTIONS,而后针对服务器的响应,做上述跟简略申请一样雷同的判断:

    (1)如果不能跨域,则理论申请不会发送

    (2)如果可能跨域,则理论申请会进行发送,进行后续业务解决

值得阐明的是,浏览器在跨域的状况下,申请都会发送进来 ,然而对于响应会判断是否满足跨域条件,如果不满足,则报错,阻止 JS 后续的执行流程,例如读取响应数据等。也就是说, 跨域机制次要是阻止数据的跨域获取,不是阻止申请的发送

3.3 简略申请

实际上浏览器将 CORS 申请分成两类:简略申请(simple request)和非简略申请(not-so-simple request)。

只有同时满足以下条件,就属于简略申请,一般来说,只须要满足前两个即可

  • 申请办法是如下三种办法之一:GET、POST、HEAD
  • HTTP 音讯头不超过如下几个字段:

    Accept

    Accept-Language

    Content-Language

    Last-Event-ID

    Content-Type:只限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/plain

  • 申请中的任意 XMLHttpRequestUpload 对象均没有注册任何事件 ** 器
  • XMLHttpRequestUpload 对象能够应用 XMLHttpRequest.upload 属性拜访。申请中没有应用 ReadableStream 对象

对于简略申请,浏览器会间接发动 CORS 申请,将理论申请发给服务器,服务器返回响应给浏览器,同时在响应头域中携带 CORS 相干头域,供浏览器进行跨域判断。

3.4 非简略申请

非简略申请时指那些对服务器有特殊要求的申请,比方申请办法是 PUT 或 DELETE,或者 Content-Type 的类型是 application/json。简而言之,不是简略申请的 HTTP 申请,都是非简略申请。

非简略申请的 CORS 申请,会在正式通信之前,应用 OPTIONS 办法发动一个预检(preflight)申请到服务器,浏览器先询问服务器,以后网页所在的域名是否在服务器的许可名单之中,以及能够应用哪些 HTTP 办法和头信息字段。只有失去必定回答,浏览器才会收回正式的 XMLHttpRequest 申请,否则就报错。

3.5 CORS 相干头域

那么,无论是简略申请,还是非简略申请,浏览器都会对响应头域中的 CORS 相干字段进行判断,CORS 的常见字段有如下几个:

3.5.1 Access-Control-Allow-Origin(必选)

波及简略Http 申请、非简略 Http 申请

含意:容许的域名,只能填 *(通配符)或者单域名

举例

从 https://www.huaweicloud.com 网页,发送 https://portal.huaweicloud.com 申请,如果服务器响应头域中没有填写 Access-Control-Allow-Origin,浏览器会报错:

或者取值不为 https://www.huaweicloud.com,浏览器也会报错:

填写为 * 或者 https://www.huaweicloud.com,则不会报错:

3.5.2 Access-Control-Allow-Credentials(可选)

波及简略Http 申请、非简略 Http 申请

含意:示意是否容许发送 Cookie,只有一个可选值:true(必为小写)。如果不蕴含 cookies,请略去该项,而不是填写 false。这一项与 XmlHttpRequest 对象当中的 withCredentials 属性应保持一致,即 withCredentials 为 true 时该项也为 true;withCredentials 为 false 时,省略该项不写。反之则导致申请失败。

举例

当 XmlHttpRequest 中设置了 withCredentials 为 true,如果服务器响应里没有 Access-Control-Allow-Credentials 字段,则浏览器会报错:

特地的,当 XmlHttpRequest 中设置了 withCredentials 为 true 时,还要求 Access-Control-Allow-Origin 字段不能为通配符 *,其实这也好了解,因为设置了 withCredentials,示意容许跨域发送 Cookie,如果 Origin 容许为 * 的话,安全性就会大大降低了,很容易结构跨站攻打:

3.5.3 Access-Control-Expose-Headers(可选)

波及简略Http 申请、非简略 Http 申请

含意 :CORS 申请时,XMLHttpRequest 对象的 getResponseHeader() 办法只能拿到 6 个根本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其余字段,就必须在 Access-Control-Expose-Headers 外面指定。

举例

在 JS 代码中,通过 XMLHttpRequest 对象来获取响应中的 wise_traceid 头域,如:

xhr. getResponseHeader(“wise_traceid”)

如果在服务器响应中,没有携带 Access-Control-Expose-Headers 或者 Access-Control-Expose-Headers 的值不蕴含 wise_traceid,则浏览器会报错,JS 拿到的值也是 null:

3.5.4 预检申请 preflight

根据上述剖析,如果是非简略 Http 申请,浏览器会先发送一个预检申请,要求服务器进行确认。预检申请应用的办法是 OPTIONS,示意这个申请是用来询问的,头信息外面,关键字段是 Origin,示意申请来自哪个源。

除了 Origin 字段,” 预检 ” 申请的头信息包含两个非凡字段:

  • Access-Control-Request-Method

该字段是必须的,用来列出浏览器的 CORS 申请会用到哪些 HTTP 办法,如 PUT:

  • Access-Control-Request-Headers

该字段是一个逗号分隔的字符串,指定浏览器 CORS 申请会额定发送的头信息字段,当应用了简略申请那 5 个字段之外的字段,浏览器会在 OPTIONS 申请头域中,指定 Access-Control-Request-Headers 的取值,如:

如果预检申请的响应,浏览器没有校验通过,不容许跨域,浏览器除了会在控制台报错之外,后续理论申请也不会发送了。

3.5.5 Access-Control-Allow-Methods(必选)

波及非简略Http 申请

含意:容许跨域申请的 http 办法(如POSTGETOPTIONS、PUT、DELETE),该字段是对于预检申请中的 Access-Control-Request-Method 的回复。

备注:对于简略申请的 GET、POST 办法,该字段不是必选的,浏览器会默认容许这两个办法进行跨域

举例

从 https://www.huaweicloud.com,跨域拜访 https://portal.huaweicloud.com,HTTP 办法为 POST,如果服务器响应里没有 Access-Control-Allow-Methods,跨域申请可能胜利:

如果服务器响应里 Access-Control-Allow-Methods 不蕴含 POST,跨域申请也能胜利:

如果申请为 PUT 办法,但响应里没有携带 Access-Control-Allow-Methods 或者取值不蕴含 PUT,浏览器会报错:

3.5.6 Access-Control-Allow-Headers(可选)

波及非简略Http 申请

含意:该字段指定了跨域容许设置的非简略 Http 申请头(5 个简略 Http 申请头之外的头域),(当预申请中蕴含 Access-Control-Request-Headers 时必须蕴含)– 这是对预申请当中 Access-Control-Request-Headers 的回复,和下面一样是以逗号分隔的列表,能够返回所有反对的头部。

举例

如果在 XMLHttpRequest 中设置了 wise_groupid 字段,而服务器响应中,没有 Access-Control-Allow-Headers 头域,或者 Access-Control-Allow-Headers 头域的值不蕴含 wise_groupid,则浏览器会报错:

3.5.7 Access-Control-Max-Age(可选)

波及简略Http 申请、非简略 Http 申请

含意:用来指定本次预检申请的有效期,单位为秒。例如,Access-Control-Max-Age 被设置为 1728000,示意有效期是 20 天(1728000 秒),即容许缓存该条回应 1728000 秒(即 20 天),在此期间,不必收回另一条预检申请。

4 其它跨域伎俩

4.1 JSONP 跨域

在 HTML 文档中,有一个 script 标签,该标签个别会援用以后 HTML 文档须要加载的 js 脚本,例如:

浏览器在加载 HTML 文档时,会程序加载 script 标签中 src 的地址,这种加载是能够跨域加载的,浏览器不会阻止跨域的 js 加载。

此外,还有 img 等标签,能够跨域加载。

而 JSONP 正是利用了 script/img 等标签可能跨域加载,来实现跨域申请的性能,如下代码所示:

代码先定义一个全局函数,而后把这个函数名通过 callback 参数增加到 script 标签的 src,script 的 src 就是须要跨域的申请,而后这个申请返回可执行的 JS 文本:

因为它是一个 js,并且曾经定义了 upldateList 函数,所以能失常执行,并且跨域的数据通过传参失去。这就是 JSONP 的原理。

如下图所示,服务器返回了响应之后,js 办法 updateList 就能够获取到响应内容,打印在控制台:

JSONP 形式跨域拜访的时候,还会携带域名的 Cookie:

从下面能够看到,JSONP 形式跨域,会不受同源政策影响,并且会携带跨域域名的 Cookie,同样也会存在平安危险。

因为 JSONP 是利用 script/img 等标签来实现跨域,而浏览器加载这些标签,应用的是 GET 办法,这就要求业务对于一些重要的申请,不可能应用 GET 办法提交数据,必须要应用 POST 办法,这样就无奈利用 JSONP 进行跨域申请了。

4.2 服务端转发

如上图所示,服务端转发实现跨域拜访的基本原理是,将拜访 B 域名的申请,通过拜访 A 域名,由 A 服务器转发给 B 服务器:

举例:

在 https://www.huaweicloud.com 页面上,想要拜访 https://portal.huaweicloud.com/v1/template 接口,那么能够通过如下伎俩实现跨域:

  • 页面上调用 https://www.huaweicloud.com/portal/v1/template 接口
  • www.huaweicloud.com 服务器,对于 /portal 结尾的申请,对立去掉 portal 门路,转发给 portal.huaweicloud.com 服务器,相当于 www.huaweicloud.com 服务器做了反向代理

然而应用该办法,会导致无奈发送 portal.huaweicloud.com 域名下的 cookie,因而利用场景无限。

5 扩大

5.1 为什么要辨别简略申请和非简略申请?

依照上文介绍:

简略申请 就是满足办法是 GET、POST、HEAD,头域为 Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type,且 Content-Type 只限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/plain 的申请,比方一般的提交 HTML Form 表单的申请。

非简略申请,就是一般 HTML Form 无奈实现的申请。比方 PUT 办法、须要其余的内容编码方式、自定义头之类的。

对于服务器来说:

第一,许多服务器压根没打算给跨站拜访应用,不会给 CORS 响应头,浏览器也会做相应报错,然而因为跨域拜访不会阻止申请收回,然而申请自身可能曾经造成了结果,所以最好可能阻止跨站申请收回。

第二,要答复某个申请是否承受跨域拜访,可能波及额定的计算逻辑,这个逻辑可能简略,如一律放通;但也可能简单,可能取决于哪个资源、哪种操作、来自哪个 Origin。对于浏览器来说,它只须要晓得是否跨域拜访,然而对于服务器来说,计算成本可大可小。所以咱们心愿这种判断不须要每次由服务器进行计算。

CORS 的 预检申请 preflight 就是这样一种机制,浏览器先独自申请一次,询问服务器某个资源是否能够跨站拜访,如果不容许的话,就在预检申请的响应中告知浏览器,使得浏览器不再发送理论申请。

这个机制即为“先许可,再申请”,因而默认禁止了跨站申请。

如果容许的话,浏览器才会持续发送理论申请,这样不非法跨站申请就不会对服务器造成任何影响。

然而这种机制,只能限于非简略申请。在解决简略申请的时候,如果服务器不打算承受跨站申请,不能依赖 CORS 预检申请 preflight 机制,因为一般表单会间接发动理论申请,所以默认禁止跨站的简略申请是做不到的。

因而,咱们常在平安标准中看到,不要应用GET 办法来提交重要敏感数据,不要应用简略的表单申请来提交敏感数据等,起因就在这里。

6 总结

浏览器跨域有三大形式,AJAX 申请跨域、JSONP 跨域、服务端转发跨域,每种跨域会实用于不同的业务场景。

其中,AJAX 跨域应用场景较多,遵循 W3C 规范,由浏览器和服务器依据 HTTP 头域 Access-Control 结尾的相干字段协商解决跨域流程。

HTTP 申请还分为简略申请和非简略申请,在非简略申请的跨域拜访时,还会触发预检申请 preflight 流程。

对于咱们业务开发和测试的启发

对于重要敏感数据,不要应用 GET、简略表单提交等简略 HTTP 申请来解决,须要应用非简略申请来解决,这样就没法通过 JSONP 等跨域伎俩来攻打获取敏感数据。

此外,除了不应用简略申请之外,还能够通过每次申请应用不同随机串、减少验证码形式二次校验等办法,来避免申请被跨站伪造。

7 参考资料

[1] 浏览器同源政策及其躲避办法

[2] 彻底了解浏览器的跨域

[3] 为什么跨域的 post 申请辨别为简略申请和非简略申请和 content-type 相干?

[4] 浅谈 CSRF 跨域攻打

深刻了解浏览器跨域原理(演示程序).rar(1.38 MB, 0 downs)

点击关注,第一工夫理解华为云陈腐技术~

正文完
 0