同源政策(same-origin policy)

前言

浏览器同源政策(Same-origin policy) 是浏览器安全的基石。它是前端开发人员必须要理解的一个知识点,也是后面各种实现跨域资源访问方式学习的基础。在接触这方面的知识时个人思考很多,在此记录一下。只能看作是个人的当前理解,并不代表一定准确,如果有误,希望指出。

1. 同源的定义

学习同源政策之前我们先明确一下同源的概念。在浏览器中,如果两个页面(请求)拥有相同的协议、域名和端口,那么这两个页面就属于同一个源(origin)。其中只要有其中一个不相同,就是不同源。例如网址 http://www.tkop.com/dirname/home 与下列各网址的同源关系分析如下。

  1. http://www.tkop.com/dirname3/admin (同源)
  2. http://www.yangchen.com/dirname/home (不同源,域名不相同)
  3. http://v1.www.tkop.com/dirname/home (不同源,域名不相同)
  4. http://www.tkop.com:81/dirname/home (不同源,端口号不相同)
  5. https://www.tkop.com/dirname/home (不同源,通信协议不同)

2. 同源政策对 ajax 请求的限制

同源政策由 Netscape 公司于 1995 年提出,其目的是为了保证用户信息的安全,防止恶意网站窃取数据。最初的同源政策是指 A 网站在客户端设置的 Cookie 在 B 网站(非同源页面)不能访问。

但是随着互联网的发展,同源政策也越来越严格(限制越来越多)。其中还规定了在不同源的情况下,无法向非同源地址发送 ajax 请求(ajax 请求限制),如果发请求,浏览器就会报错。通俗地讲就是 ajax 只能向自己地服务器发送请求。例如现有 A、B 两网站(服务器),A 网站中的页面只能向 A 服务器发送 ajax 请求,B 网站中的页面只能向 B 服务器发送 ajax 请求。但是浏览器是不允许 A 网站中的页面向 B 服务器发送 ajax 请求的(其实是允许的,不过拒绝接受处理响应),同理,也不允许 B 网站中的页面向 A 服务器发送 ajax 请求。 以上都是理论,下面我演示一下同源政策的 ajax 限制。

1、首先服务器端创建两个监听不同端口的服务。注意 A 监听 3000 端口,B 监听 3001 端口,两个服务不同源。

// 引入express框架
const express = require('express');
// 引入路径处理模块
const path = require('path');

// 创建 A、B 两个服务
const A = express();
const B = express();
// 开放两服务器的静态文件
A.use(express.static(path.join(__dirname, 'publicA')));
// 虽然只是演示,但为让大家不搞混淆还是将静态文件放到两个不同的文件夹
B.use(express.static(path.join(__dirname, 'publicB')));

// B服务的一个get请求处理路由
B.get('/example', (req, res) => {
    res.send('来自B的响应');
});

// 分别监听不同端口(不同源)
A.listen(3000);
B.listen(3001);
console.log('服务已开启,需访问A、B服务器可访问本机不同端口');

2、创建服务器端服务后在 A 服务页面 aaa.html 中向 B 服务器发送 ajax 请求。这里为了方便没有使用 jquery 的 $.ajax() 发送请求或者原生 js 发送请求,而是使用了前面自己封装的 ajax() 函数。

在这里插入图片描述
3、访问地址 http://localhost:3000/aaa.html ,即访问 A 浏览器的静态页面。再向 B 浏览器发送 ajax 请求,查看浏览器对请求的处理和响应的处理。可以看到浏览器首先会出现如下报错信息。

在这里插入图片描述
使用我蹩脚的英语水平翻译过来就是:请求资源头信息中不存在 “Access-Control-Allow-Origin” 字段信息。从源 “http://localhost:3000” 访问 “http://localhost:3001” 的 ajax 请求已被同源政策阻止。

看看这次请求的请求头信息和响应头信息,如下图所示:

在这里插入图片描述
为了对比我们可以在 B 网站的 bbb.html 页面中发送一个相同的 ajax 请求(没有跨域),我将它的请求信息截图如下,方便与上图对比:

在这里插入图片描述
通过上图,比较一下跨域和不跨域的情况下请求报文和响应报文中头信息的一些字段的值的差异。

  • 跨域请求的请求头信息中的 Sec-Fetch-Site: same-site ,而非跨域请求的请求头信息中的 Sec-Fetch-Site: same-origin 。所以这是表示这次请求是否属于跨域请求。

  • 在跨域请求的请求头信息中则增加了 Origin 字段,值为发出请求的页面(aaa 的脚本)的协议、地址和端口

  • 然后比较一下响应头信息,发现它们一模一样,其实这算是同源政策的本质的一个体现。同源政策本质上是浏览器客户端为维护用户数据安全而设定的,与服务器端没有太大的联系(个人觉得唯一联系可能就是服务器可以提供是否允许跨域请求数据或者访问 cookie 等权限的验证信息供浏览器进行验证),这也是为什么我们将其称为浏览器的同源政策即无论是否涉及跨域,浏览器按照要求正常发出请求,服务器接收请求并给出响应,浏览器也能接收到响应。但是浏览器根据同源政策有权决定是否将响应数交由页面脚本处理(或者报错)

  • 为什么我敢给出上面的结论呢?你可以将注意力放在响应头信息中的 Content-Length 字段。这个字段显示的正是服务器响应内容的字节长度,例如上面的响应是字符串 “来自B的响应”,英文字母 B 是 1 个字节,每个汉字 3 个字节。这也就说明了即使跨域访问也确实可以接收到服务器的响应信息。

上面我们已经清楚了同源政策对 ajax 的限制,但是浏览器的同源政策的限制不止这个。总结如下:

  • Cookie、LocalStorage 和 IndexDB 无法读取。
  • DOM 无法获得。
  • AJAX 请求无法发送。

在此我的笔记只会涉及 Cookie 无法获取和 ajax 请求无法发送的问题,其他的不会进行学习记录。不知道你是否已经了解学习过有关 Cookie 和 Session 的相关知识,都学到跨域资源共享了我觉得应该是没问题了。

3. 小对话(sessions)

为了更加深刻地理解浏览器地同源政策,我自己参考自己地思考编了下面一小段会话。A 代表 A 服务器,B 代表 B 服务器,aaa 代表上面的 aaa.html 页面(A 服务器的页面),bbb 代表 B 服务器上的 bbb.html 页面(B服务器上的页面,假设是登录页面),小刘代表浏览器。并假设 B 是我现在使用的 CSND 服务器,A 是某恶意网站。

用户在浏览器输入 B 网站首页地址。。。

用户:小刘你去跟 B 要一下它们首页的数据。

小刘跟 B 要了数据并将登录(bbb)页面渲染展示给用户。。。用户输入登录信息。。。

用户:登录账号密码填好了,小刘你让 B 瞧瞧看看我能不能登录。

B 验证用户输入的账号密码。。。确认通过。。。

B:小刘你跟用户说他登录成功了(响应数据),还有这是他的登录证明(Cookie),保存好,下次他跟我要东西(发请求)的时候带上它。我是记不住他的。

用户没有退出登录,又在浏览器输入A 网站的访问地址并请求到了 aaa 页面。。。

如果没有同源政策 aaa(其中添加的恶意脚本)可以怎么样?

aaa:小刘你去跟 B 要一下用户的用户的电话号码。

小刘此时并未察觉 aaa 不是 B 网站的页面脚本。小刘没管那么多就带着 B 给 Cookie 跟 B 要用户的电话号码。。。

小刘:B 哥这是你上次给用户的登陆证,他现在要看看电话号码,你给我吧。

B 看了看 Cookie 确实是自己发出去的,并根据 Cookie 里面的信息比如用户 ID 找到了用户的号码,并将它给了小刘。

小刘:aaa 这是你要的东西,不用谢。
aaa:小刘你再去跟 B 说我要删掉所有的博客内容并注销账户。

小刘此时还是未察觉 aaa 不是 B 网站的页面脚本,又带着 Cookie 过去了

B:什么!!!他要注销?好吧,登录证明(Cookie)你都带过来那证明他确实登录过了也有权进行这些操作了,那注销掉吧。

看到这我们发现如果没有同源政策会出现两个问题:

  • 浏览器不会根据是否同源去判断是否携带 B 给用户设置的 Cookie 来对 B 进行访问(或者说判断 aaa 是否有权访问使用这份 Cookie),导致服务器误判。
  • 浏览器也不会根据是否同源判断脚本是否有权力获得服务器响应的数据。

根据这两点我们大概也就理解了同源政策的本质。如果有同源政策那上面的对话会变成怎么样呢?

aaa:小刘你去跟 B 要一下用户的用户的电话号码。

小刘看了看 aaa 的请求地址中的协议、IP 和端口。嗯?发现它和 B 不是同源的,也不知 B 哥允许它请求数据不。将它记在笔记本(Origin)上先。但是 B 哥上次给的登录证(Cookie)就默认不带了。

小刘:B 哥有个哥们想向你要电话号码。

B 看看小刘没带登录证说明他那哥们没登陆过呀,给你返回些没登录过的数据吧(叫他先登录)。

B:小刘这是你可以给他的数据,还有这是能够得到这些数据的白名单(Access-Control-Allow-Origin),还有这是我能接受他们(白名单内的人)的请求方式(Access-Control-Allow-Methods),如果他们在名单内就给他们数据吧,你自己决定。

小刘拿到哪份名单发现 aaa 的 IP 和协议虽然一样但是端口不一样。。。。当然 B 哥也许没有给他那份名单(例如我们的示例),那连给都没给说明不允许跨域了。

小刘:aaa 对不起呀,B 并不允许你获取他的数据。他提供的数据我不能给你(报错)。

上面的某些内容涉及到了跨域资源共享的部分知识和有关跨域 Cookie 携带问题。后面会详细介绍(相关笔记写完贴链接)。反正上面就大致体现出了同源策略的重要性,并不是说他可以解决掉所有问题,使网站绝对安全(谁都做不到,不知道你有没有看过没有绝对安全的系统这个电影),例如恶意网页可以伪造Origin头信息,绕过服务端对Origin头的检查等。

下面是我在一个有关文章下面看到的一个有关评论,觉得不错所以截了下来。

在这里插入图片描述
以上就是我对浏览器同源政策的理解,如果对你有帮助希望可以给我点个赞(鼓励一下不断进行知识输出的人儿)。后面解决一下跨域资源访问的问题,涉及的知识有 JSONP、CORS 和 WebSocket

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值