浏览器(Web)页面安全的基石:同源策略

浏览器(Web)页面安全

首先在了解浏览器页面安全策略之前,我们先设想一下,假设浏览器没有安全策略,那么我们天天接触的Web世界将会是什么样子?

假设我们访问的站点可以任意加载并执行其他站点的脚本、图片、音视频等资源,如果当前访问的正好是一个银行网站,然后接着我们又打开了另外一个黑客恶意网站,由于没有浏览器安全策略的限制,那么这个黑客恶意网站可以做很多事情,比如:

  • 可以直接修改银行站点的DOM和CSSOM等信息,
  • 可以直接读取银行站点的cookie等信息,并伪造请求发起转账
  • 劫持用户的登录名和密码
  • 可以生成一个script标签插入恶意脚本,通过注入的恶意脚本将以上信息通过Ajax请求发送到自己的服务器
  • ...

你会发现,在没有安全策略保障的互联网世界中,用户是没有隐私和数据安全的,所以浏览器必须制定一系列安全策略来保障安全,约束以上不安全的行为。
所以,浏览器页面中最基础也是最核心的安全策略:同源策略就登场了。

同源策略(Same-origin policy)

说到同源策略,这似乎是一个前端开发耳熟能详的名词,因为在各大面经和前端资料中只要是涉及网络、Ajax请求、前端攻击、跨域的地方都会出现它的名字,可见其重要性。
要想搞懂同源策略,我们需要明确以下几个问题:

  1. 什么是同源?
  2. 什么是浏览器的同源策略?具体表现在哪些方面?
  3. 浏览器都出让了同源策略中的哪些安全性?

1. 什么是同源?

如果两个URL的协议、域名和端口号都相同,那么我们就称这两个URL或者这两个站点是同源的。比如:

https://leetcode.cn/problems/3sum/
https://leetcode.cn/problems/two-sum/

以上两个URL,其协议都是HTTPS、域名都是leetcode.cn、端口号都为https协议默认的443,所以我们称以上两个URL是同源的。

2. 什么是浏览器的同源策略?

在了解了什么是同源之后你可能会问:就算两个页面是同源的然后呢?有什么好处呢?或者说相对于不同源的页面有什么优点呢?

首先你需要知道如果两个页面是同源的,那么浏览器默认是允许这两个相同的源之间相互访问资源和操作DOM的,如果两个页面的源不相同,那么要互相访问资源和操作DOM是有一套浏览器自己的限制策略的,我们把这套基于站点是否同源而产生的策略叫做同源策略。

具体来讲,浏览器的同源策略主要表现在3个方面:

1. DOM层面(读取和写入DOM能力)

同源策略规定来自不同源的页面其DOM对象不可以通过js脚本去读取和写入,反之如果两个站点同源,那么它们之间可以互相访问和操作DOM。

比如我在baidu.com/music页面通过调用window.open()方法或者点击按钮跳转到了baidu.com/news页面,由于这两个页面是同源的,所以我们可以在news页面去操作和访问music页面的DOM,具体的方法是通过news页面的对象opener获取到指向原来第一个页面music的全局对象,然后通过操作opener的方法控制第一个页面中的DOM。

// 在news页面通过opener获取到music页面的顶级对象 也就是global对象
let document = opener.document;

// 通过DOM方法将news页面的doucment全部隐藏
document.body.style.display = "none"

但是,如果两个页面不是同源的,比如从163.com通过调用window.open()方法跳转到baidu.com,此时在baidu.com中虽然可以获取到opener对象,但是在进一步去访问原来163.com页面中的document的时候却会报错,这就是浏览器的同源策略进行了限制:

Uncaught DOMException: Blocked a frame with origin "https://www.baidu.com" from accessing a cross-origin frame.

2. 存储数据层面(读取和写入cookie、Storage数据能力)

除此之外,同源策略还限制了不同源的站点间不可以读取cookie、LocalStorage、SessionStorage等数据,相反如果是同源的那么它们间的这些数据都是可以互相读取的。
如果两个站点不同源,通过opener对象去读取另外一个站点的资源就会报上面一模一样的错误:

Uncaught DOMException: Blocked a frame with origin "https://www.baidu.com" from accessing a cross-origin frame.

3. 网络层面(请求资源的能力)

同源策略规定了如果两个站点非同源(也叫作跨域),此时A站点直接发起了对于B站点的Ajax请求,那么同源策略会限制这种跨域请求并报错提示:

Access to fetch at 'https://A.com/api/data' from origin 'https://www.baidu.com' has been blocked by CORS policy: 
The 'Access-Control-Allow-Origin' header has a value 'https://time.geekbang.org/' that is not equal to the supplied origin. Have the server send the header with a valid value, or, if an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

这段错误信息告诉我们两个事情:
第一你访问的资源跨域了;
第二如果你还是想访问也不是不可以,需要利于跨域资源共享的能力让服务端配置名称为'Access-Control-Allow-Origin'的响应头为你的源Origin.

3.浏览器都出让了同源策略中的哪些安全性?

同源策略虽然会隔离不同源之间的DOM、页面数据和网络通信,进而保障了我们的页面安全。但是和安全对立的是便利性,假设我们将不同的源之间绝对隔离,这肯定是最安全的做法。但是这会让我们在开发web项目的时候非常不方便,举两个栗子:

  1. 按照同源策略的要求,我们应该将一个web项目中所使用的js脚本、html、css、图片、音频视频等静态资源全部放在同一台服务器上,很显然当并发增大的时候这种方案不可取;
  2. 如果我们想把静态资源部署在CDN服务器上以加快用户访问资源的速度的话,CDN服务器节点上的资源部署的域名肯定和源服务器的域名不一致,很显然这不合适;

所以为了开发便利和跨站点通信的方便,浏览器就权衡利弊,出让了一些同源策略的安全性,然而由于出让安全性又引起了两个非常常见的攻击手段,这个后面会说到。
这里首先说先浏览器分别出让同源策略的哪些安全性?

1. 页面中可以嵌入来自不同源的资源

  • 为什么要这样做?
    因为同源策略要求我们页面上的所有资源都来自一个源,通过上面的说明,这很显然是不可能的。
    第一:页面中所有资源不可能都部署在一台服务器上;
    第二:如果要配置CDN服务器,人家的服务器肯定和源服务器不是一个源;

所以浏览器就允许页面可以嵌入第三方的资源,这部分第三方资源是可以通过不同源的站点加载并通过浏览器在当前页面执行的。

  • 问题:XSS攻击
    但是这种做法会导致一个非常严重的问题那就是XSS攻击,黑客通过哪些途径将恶意脚本嵌入到HTML页面中,然后就可以通过恶意脚本中的js代码来收集当前页面的cookie、storage等数据,劫持用户的输入敏感信息,然后通过CORS将当前页面的数据通过Ajax请求发送给黑客服务器导致用户信息泄漏。这只是一个很简单的XSS攻击的例子,具体的XSS攻击后面会详细阐述。

  • 解决方案:内容安全策略CSP
    而为了解决引入第三方资源导致XSS攻击的问题,浏览器又加入了内容安全策略CSP(Content Safe Policy),其核心思想就是让当前站点的服务器决定浏览器可以加载哪些资源,让服务器决定浏览器是否可以执行内联的js脚本代码,CSP在一定程度上可以大大降低XSS攻击。关于CSP的详细信息及其配置指令后面会有一篇专门的文章去说明。

2. 可以通过跨域资源共享策略发起跨域Ajax请求

由于同源策略的限制,当前页面只能向自己的源发起Ajax请求,而对于非同源的站点发起的请求都会由于跨域而返回错误。
但是在实际的开发中这样子会大大的制约我们的生产力,基于这个需求浏览器允许当前页面可以通过跨域资源共享CORS策略发起跨域资源安全访问。

3. 通过跨文档消息机制window.PostMessage解决了不同源的DOM以及cookie等通信的问题

由于同源策略的限制,如果两个页面是不同源的,那么它们之间是无法进行通信的,也就是无法互相操作DOM等消息。
所以浏览器又引入了跨文档的消息通信机制:window.PostMessage接口来和不同源的DOM进行通信,这就解决了不同源的页面之间数据安全通信的问题。

posted @ 2022-09-10 16:07  小高同学1997  阅读(159)  评论(0编辑  收藏  举报