上一章我们学习了关于用户交互内容的第三部分,其中包含了页面查找、关闭请求与监听器,以及拖放的相关内容,相信让许多同学对于用户的交互有了更深刻的了解。
这一章的内容会跟源 Origin,以及跨域紧密相关,说到 Origin 和跨域,相信大家都不陌生,很多人仅仅停留在表面,其实它们还有很多相关的内容,这一章就让我们一起深挖一下它们吧~
源(Origins)
源是 Web 安全模型的基本单位。在 Web 平台中,具有相同源的两个实体被认为是互相信任的,并拥有相同的权限。而具有不同源的实体则被视为潜在的敌对关系,彼此之间会有不同程度的隔离。
例如,如果某网站 A(托管在 a.example.com)试图查看某网站 B(托管在 b.example.org)的 DOM 内容,则会引发一个 "SecurityError" 的 DOMException 异常。
Origin 的类型可以是以下两种:
- 不透明源(Opaque Origin)
-
- 这是一种特殊的源类型,它只能用来进行相等性测试,即只能用来判断两个不透明源是否相同,而无法像其他 Origin 那样直接访问或操作。例如含有 sandbox 属性的 iframe 元素会生成一个不透明源。
-
- 可通过含有 sandbox 属性的 iframe 元素、从
data:或blob:URLs 加载的资源等形成一个不透明源。
- 可通过含有 sandbox 属性的 iframe 元素、从
- 元组源(Tuple Origin)
-
- 元组包含以下组成部分:
-
-
- 协议(scheme) :一个 ASCII 字符串。
- 主机(host) :主机名,可以是域名、IP 地址、或者空字符串。
- 端口(port) :可以是
null或 16 位的无符号整数,所以端口号的范围是 0 ~ 65535。咱们常用的 8080、3000、3306、6379 等端口都在这个范围内。 - 域名(domain) :可用于子域共享的域名,可以是
null或一个域名,只能通过document.domainAPI 修改。
-
大家可以看到,源其实并不是只有我们平时所说的协议、域名、端口,完整的应该是协议、主机、端口和域名 (只是这里的域名并不常用,未来将会被淘汰) 。而我们平时经常所说的域名,其实就是这里的主机。
为了避免歧义,下面我都会用规范里的用词,我们常说的域名,会用规范里的主机(host) 来代替,而规范里的域名(domain) 是另外一个相关概念。
源通常是不可变的,元组源的唯一可变的部分是域名,且只能通过 document.domain API 修改。
对一个 Origin 进行序列化,得到的结果是:
- 如果源是不透明源,返回
null。 - 否则返回
协议://主机名[:端口]
例如,源 ("https", "xn--maraa-rta.example", null, null) 的序列化结果为 "https://xn--maraa-rta.example"。
同源(same origin)算法
判断两个 Origin A 和 B 是否是同源,遵循以下规则:
- 如果 A 和 B 是同一个不透明源,则返回
true。 - 如果 A 和 B 都是元组源,并且它们的
scheme、host和port都相同,则返回true。注意这里并没有判断 domain。 - 如果以上条件都不满足,则返回
false。
同源域(same origin-domain)算法
判断两个 Origin A 和 B 是否是同源域,遵循以下规则:
- 如果 A 和 B 是同一个不透明源,则返回
true。 - 如果 A 和 B 都是元组源,则判断:
-
- 如果它们的
scheme和domain相同,且domain非空,则返回true。注意这里并没有判断 port。 - 否则,如果 A 和 B 是同源的,并且它们的
domain都为null,则返回true。
- 如果它们的
- 如果以上条件都不满足,则返回
false。
给大家列一个表格,总结一下上面说到的同源和同源域算法。
| A | B | 同源 | 同源域 |
|---|---|---|---|
("https", "example.org", null, null) | ("https", "example.org", null, null) | ✅ | ✅ |
("https", "example.org", 314, null) | ("https", "example.org", 420, null) | ❌ | ❌ |
("https", "example.org", 314, "example.org") | ("https", "example.org", 420, "example.org") | ❌ | ✅ |
("https", "example.org", null, null) | ("https", "example.org", null, "example.org") | ✅ | ❌ |
("https", "example.org", null, "example.org") | ("http", "example.org", null, "example.org") | ❌ | ❌ |
站点(site)
说到同源,大家可能会想到 cookie 中的 sameSite 属性,可以让相同的站点共享 cookie。
那么大家有没有想过,怎么样才算作 same site,也就是同站呢?
顶级域名、二级域名、三级域名
先给大家科普一下什么是域名中的顶级域名、二级域名和三级域名。
顶级域名(Top Level Domain, TLD)
顶级域名是域名的最高层级,位于最右侧的一部分。常见的顶级域名有 .com、.org、.net、以及国家/地区代码顶级域名如 .cn(中国)、.jp(日本)等。顶级域名由互联网域名分配机构(如 ICANN)管理。
例如在 baidu.com 中,.com 就是顶级域名。
二级域名(Second Level Domain, SLD)
二级域名是顶级域名的左边一部分,一般由用户注册的名称构成。二级域名通常是网站的主要标识,且直接位于顶级域名之下。
例如在 baidu.com 中,baidu 就是二级域名。大家注意,二级域名是不带上后面的顶级域名的。
三级域名(Third Level Domain,也叫作 Subdomain)
三级域名位于二级域名左侧,是二级域名的一个子域。网站可以在二级域名的基础上创建多个三级域名,以区分不同的服务或子站点。它可以进一步划分不同的网站部分或应用服务,例如 www、mail 等。
在 www.baidu.com 中,www 是三级域名;在 map.baidu.com 中,map 也是一个三级域名。大家注意,三级域名是不带上后面的二级域名和顶级域名的。
大家可以看看下图,来帮助理解和记忆。
这里提一下,因为翻译原因,有的人会将 Second Level Domain 翻译为一级域名,从而将 Third Level Domain 翻译为二级域名,其实它们都代表同一个东西,只是翻译不一样导致结果不一样,大家可以在面试的时候提一下,并且用英文名称可以避免歧义。
介绍
一个站点的域名,必须是要符合规则的,必须要含有公共后缀(Public Suffix List,这里列举了所有的公共后缀),例如 a.b.c.com,而不能是随意的例如 a.b.c.helloworld。
于是就有了一个名词:可登记域名(registrable domain) ,表示可以登记的有效域名,只有符合规则的域名才可以登记以供大家从互联网访问。
获取可登记域名的值的步骤为:
- 如果主机的公共后缀是 null,或者主机就等于公共后缀,则返回 null
- 返回域名中公共后缀前面的单个部分字符串 + 公共后缀(也就是二级域名 + 顶级域名) ,如果主机末尾带有一个
.,则可登记域名结尾也会拼接上一个.。
看起来有点难以理解,大家看看下面的例子应该就明白了:
| 主机 | 公共后缀 | 可登记域名 |
|---|---|---|
com | com | null |
example.com | com | example.com |
www.example.com | com | example.com |
sub.www.example.com | com | example.com |
EXAMPLE.COM | com | example.com |
example.com. | com. | example.com. |
github.io | github.io | null |
whatwg.github.io | github.io | whatwg.github.io |
[2001:0db8:85a3:0000:0000:8a2e:0370:7334] | null | null |
前面提到的同站的判断依据,就是比较两个站点的 site 值是否相同。获取 site 的值的步骤为:
- 如果站点的 origin 是一个不透明源,则返回这个 origin;
- 如果 origin 的可登记域名是 null,则返回 (origin 的协议, origin 的主机);
- 返回 ( origin 的协议, origin 的可登记域名) 。
如果两个站点的 site 值是相同的,则满足 sameSite。例如 https://map.baidu.com 的 site 值根据上面步骤得到的结果为 (https, baidu.com),https://www.baidu.com 的结果为 (https, baidu.com),所以满足 sameSite 条件。
两个站点的 site 值是相同的,也就是同站(same site) ;值不同,也就是跨站(cross site) 。根据前面的公式可以得到一个结论,跨站一定跨域,跨域不一定跨站。
测试
咱们一起来做个测试,给满足 sameSite 条件的其中一个站点设置 cookie,并且设置 SameSite=strict(最严格的模式,只有满足同站才会自动携带 cookie),看看访问另外一个站点会不会自动携带 cookie。
通过 Express 框架运行两个页面,一个页面地址是 http://localhost:3000,另一个是 http://localhost:4000。根据上面获取 site 的步骤,我们可以知道这两个地址是同站的。
http://localhost:3000 页面:
const express = require('express');
const fs = require('fs');
const app = express();
const buffer = fs.readFileSync('./index.html');
const html = new String(buffer).replace(/\n/g, '');
app.use('/', (_, res) => {
res.set('content-type', 'text/html');
// 设置 Set-Cookie 响应头
res.set('set-cookie', 'name=cookie_from_port_3000; SameSite=strict');
res.send(html);
});
app.listen(3000, () => {
console.log('页面启动在 localhost:3000');
});
http://localhost:4000 页面:
const express = require('express');
const fs = require('fs');
const app = express();
const buffer = fs.readFileSync('./index2.html');
const html = new String(buffer).replace(/\n/g, '');
app.use('/', (_, res) => {
res.set('content-type', 'text/html');
res.send(html);
});
app.listen(4000, () => {
console.log('页面启动在 localhost:4000');
});
可以看到上面代码,我只给 3000 页面设置了 cookie,4000 页面并没有设置。我们来看一下结果,首先可以看到 3000 页面已经设置了 cookie,并且请求头没有 cookie。
刷新一下 3000 页面,可以看到此时请求头携带了 cookie:
我们再打开 4000 页面看一下:
可以看到,虽然 http://localhost:3000 和 http://localhost:4000 并不同源,但是他们满足同站,而 Set-Cookie 中的 SameSite 属性就是按照同站的规则来执行的。
通过 document.domain 绕开同源限制
通过 document.domain 属性可以获取当前页面用于安全检查的域。而通过设置 document.domain 属性,可以绕开同源策略的限制,从而允许同一主域下的不同子域页面相互访问对方的 DOM。
举个例子,假设有以下三个页面:
https://sub.example.com:443:一个位于子域的页面。https://example.com:443:主域页面。https://other.com:443:不同的域。
对于 sub.example.com:443 来说,默认情况下的元组源为:
- 协议:https
- 主机:sub.example.com
- 端口:443
- 域名:null(默认情况下没有设置)
在这种情况下,host 为 sub.example.com。因此,sub.example.com 仅信任自身,而不会自动信任 example.com 的其他子域。
但是如果我们设置了 document.domain,情况就不一样了。
假设在 sub.example.com 和 example.com 的页面上都执行了以下代码:
document.domain = "example.com";
此时元组源变为:
- Scheme:
https - Host:
sub.example.com - Port:
443 - Domain:
example.com
现在,domain 字段设置为 example.com。此时,https://sub.example.com 和 https://example.com 会被视为同源,因为它们共享了 domain 字段,允许在同一父域(example.com)下进行跨子域访问对方的 DOM。
只有当两个页面都将 document.domain 设置为相同的主域(如 example.com)时,才允许跨子域的同步访问。如果一方未设置,访问仍会被阻止。
虽然规范没有明确说明,但是根据我的推测,在设置了 document.domain 后,同源策略使用的应该是同源域算法,而不再是同源算法。在设置了 document.domain 后,同源策略遵循同源域算法的规则,只要协议和 domain 值相同,就视为同源。大家可以看到这里忽略了端口号。
但是大家注意,document.domain 属性已经不推荐使用了,有些浏览器仍然支持该属性,但是不保证未来会不会被废弃。
设置失败场景
document.domain 在一些特定环境中无法使用,会设置失败:
- 设置的值既不是当前的主机名,也不是父域名。例如在
a.example.com上设置 document.domain 为baidu.com,当然会设置失败。
- 在 iframe 元素的
sandbox属性中启用了沙盒,就无法使用document.domain,如果设置,将会抛出SecurityError异常。
- 对于不透明源的文档(例如从
data:或blob:URLs 加载的资源),无法设置document.domain,因为它们没有明确的主域。
- 没有浏览上下文的文档,例如通过 createHTMLDocument 创建的 Document 对象,并且其对应的元素没有挂载到页面上,也无法设置
document.domain。
- Permissions-Policy 中的
document-domain权限被禁用。
避免使用 document.domain
document.domain 最初被用来允许跨子域的通信,可以绕开同源策略的限制。但是这种用法会削弱安全性,并带来难以管理的风险:
- 削弱同源策略:将
document.domain设置为共享的基础域(例如example.com)后,多个子域(例如a.example.com和b.example.com)就可以互相访问彼此的 DOM。虽然这种方式在某些情况下很有用,但如果其中某个子域不安全,可能会带来安全隐患。
- 共享主机的安全风险:在共享主机环境中,多个应用可能共享同一个域名或 IP 地址(但在不同端口上运行),而设置了
document.domain以后会导致同源检查忽略端口号,从而引发信息泄露的风险。这样,如果一个子域或端口被入侵,就可能导致未经授权的访问。
由于这些安全问题,document.domain 正逐渐从 Web 平台中移除,这是一个较为漫长的过程,毕竟要考虑很多系统都是老代码,以及各种兼容性,但它的废弃已经在推进中了,以提高现代 Web 应用的安全性。
document.domain 的替代方案
想让具有相同父域的子域之间相互通信,我们可以使用其他方法来替代使用 document.domain:
postMessage()方法:postMessage() 可以实现安全的跨源通信。不同源的窗口或 iframe 可以使用该方法发送消息,而接收方则需要验证消息来源的 origin,以确保数据的安全性。
// 在发送窗口中
otherWindow.postMessage("Hello", "https://another-subdomain.example.com");
// 在接收窗口中
window.addEventListener("message", (event) => {
// 验证来源
if (event.origin !== "https://expected-origin.com") return;
console.log("接收到的数据:", event.data);
});
MessageChannel对象:MessageChannel 提供了一种更高级的通信方式,可以在两个窗口或 worker 之间创建专用的通信通道,实现更安全的双向通信,不依赖 document.domain。可以借助 postMessage,将 port2 传递给目标窗口,目标窗口用 port2 传递信息,主窗口用 port1 进行消息的监听。
// 在主窗口中
const channel = new MessageChannel();
iframe.contentWindow.postMessage("initiate", "*", [channel.port2]);
// 在 port1 上监听消息
channel.port1.onmessage = (event) => {
console.log("来自 iframe 的消息:", event.data);
};
跨域开放者策略(Cross-origin opener policies)
前面章节已经介绍过了一部分,这里就不再赘述重复内容了,大家可以点击链接过去再复习一下。
前面章节只介绍了跨域开放者策略 COOP 对 opener 属性的影响,这里再介绍一下对页面间通信的影响。
大家都知道两个网站之间可以使用 postMessage 或者 MessageChannel 来通信,无论两个网站是同源或非同源。就算是相同父域的子域,也不应该再通过修改 document.domain,而是使用 postMessage 或者 MessageChannel。
而对于通过 window.open 打开的跨域窗口,我们在设置了 COOP 后,会阻止源窗口与跨域窗口之间的通信,无法再使用 postMessage 和 MessageChannel。
对于 COOP 的三个值:
same-origin:打开或被打开的跨域窗口之间无法使用 postMessage 进行消息传递,同时 opener 属性也为空。同源的窗口仍然可以使用 postMessage 和 opener 属性。
same-origin-allow-popups:打开或被打开的跨域窗口之间可以使用 postMessage 进行消息传递,并且也可以使用 opener 属性(属性受限制的 Window 对象)。
unsafe-none:没有任何限制,可以通过 postMessage 进行消息传递。
测试
我们来测试一下,不同的三个值会对源窗口与打开的跨域窗口之间的通信有何影响。
unsafe-none
依然是通过 Express 框架运行两个页面,一个页面地址是 http://localhost:3000,另一个是 http://localhost:4000,我们先来看看设置了 unsafe-none,其实也就相当于没有设置,两个页面可不可以通信:
http://localhost:3000 页面:
const express = require('express');
const fs = require('fs');
const app = express();
const buffer = fs.readFileSync('./index.html');
const html = new String(buffer).replace(/\n/g, '');
app.use('/', (_, res) => {
res.set('content-type', 'text/html');
res.set('cross-origin-opener-policy', 'unsafe-none');
res.send(html);
});
app.listen(3000, () => {
console.log('页面启动在 localhost:3000');
});
<h1>3000 端口的前端创可贴</h1>
<button id="btn">打开 4000 端口</button>
<script>
btn.addEventListener('click', () => {
const popup = window.open('http://localhost:4000');
// 在 4000 页面 load 事件加载后调用,但是打开的是跨域页面,popup 所代表的 Window 对象只能访问受限的属性,无法监听 load 事件,通过 setTimeout 来模拟 4000 页面挂载完毕。
// 不使用 setTimeout,同步调用 postMessage 是不生效的,必须得等 4000 页面挂载完毕
setTimeout(() => {
popup.postMessage('你好我是 3000 端口', 'http://localhost:4000');
}, 1000);
});
</script>
http://localhost:4000 页面:
const express = require('express');
const fs = require('fs');
const app = express();
const buffer = fs.readFileSync('./index2.html');
const html = new String(buffer).replace(/\n/g, '');
app.use('/', (_, res) => {
res.set('content-type', 'text/html');
res.send(html);
});
app.listen(4000, () => {
console.log('页面启动在 localhost:4000');
});
<h1>4000 端口的前端创可贴</h1>
<script>
window.addEventListener('message', e => {
if (e.origin !== 'http://localhost:3000') {
return;
}
console.log('接收到来自 3000 端口的消息', e);
});
</script>
先来看看 3000 页面的响应头:
COOP 设置成功,我们再点击按钮打开 4000 端口页面看看:
可以看到,4000 页面可以接受到 3000 端口的消息,不受 COOP 为 unsafe-none 的影响。
same-origin
将 3000 端口 COOP 响应头设置为 same-origin:
res.set('cross-origin-opener-policy', 'same-origin');
打开 4000 页面:
可以看到,4000 页面没有接收到任何消息,所以 COOP 为 same-origin 时,打开的跨域窗口无法与源窗口通信。
same-origin-allow-popups
将 3000 端口 COOP 响应头设置为 same-origin-allow-popups:
res.set('cross-origin-opener-policy', 'same-origin-allow-popups');
打开 4000 页面:
可以看到,4000 页面可以接收到 3000 端口的消息,所以 COOP 为 same-origin-allow-popups 时,打开的跨域窗口可以与源窗口通信。
跨域嵌入策略(Cross-origin embedder policies)
嵌入策略 (Embedder Policy) 是浏览器用来控制跨域资源如何被嵌入和加载的安全机制。它通过指定策略值来决定哪些跨域资源可以被加载,以及在哪些情况下需要明确的服务器授权。以此来提升 Web 应用的安全性和隔离性。
通过 Cross-Origin-Embedder-Policy 响应头设置策略值,它的值可以是:
- unsafe-none:默认值。使用该值时,允许从其他来源加载资源,而不需要通过 CORS 协议或
Cross-Origin-Resource-Policy(跨域资源策略)响应头明确授权的情况下获取跨域资源。也就是说应用程序可以加载任意跨域资源,没有任何限制。
- require-corp:使用此值时,获取跨域资源需要服务器通过 CORS 协议或 Cross-Origin-Resource-Policy 响应头给予明确的授权,也就是说得经过服务器的授权和同意,我们才能使用他们的跨域资源。
- credentialless:使用此值时,对于跨域资源请求来说,响应头里的 Set-Cookie 不会生效,同样的对应的请求也不会携带 cookie 过去,也就是说所有跨域资源请求都以匿名方式发送,不携带
cookie。并且不需要明确的 Cross-Origin-Resource-Policy 响应头就可以获取跨域资源。
测试
咱们再来测试一下,仍然是通过 Express 框架,我们打开一个 http://localhost:3000 页面,并且设置 Cross-Origin-Embedder-Policy 响应头。
unsafe-none
const express = require('express');
const fs = require('fs');
const app = express();
const buffer = fs.readFileSync('./index.html');
const html = new String(buffer).replace(/\n/g, '');
app.use('/', (_, res) => {
res.set('content-type', 'text/html');
res.set('cross-origin-embedder-policy', 'unsafe-none');
res.send(html);
});
app.listen(3000, () => {
console.log('页面启动在 localhost:3000');
});
<h1>前端创可贴</h1>
<img
src="https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png"
alt=""
/>
我们请求了一张跨域的百度的 logo 图片,并且 Cross-Origin-Embedder-Policy 设置为 unsafe-none,我们来看一下页面:
可以看到,响应头正确设置,并且图片也正常返回,而且图片也正常携带了 cookie(我访问过百度页面,所以会携带 cookie)。所以,COEP 为 unsafe-none 时不会对跨域资源有任何影响。
require-corp
将 COEP 设置为 require-corp:
res.set('cross-origin-embedder-policy', 'require-corp');
看一下页面:
可以看到,浏览器的响应头为我们提示了错误信息,所以,当 COEP 的值为 require-corp时,跨域资源必须得有 CORS 协议或 Cross-Origin-Resource-Policy 响应头给予明确的授权才可以正常访问。
credentialless
将 COEP 设置为 credentialless:
res.set('cross-origin-embedder-policy', 'credentialless');
看一下页面:
可以看到,图片正常获取到了,但是图片的请求头里的 cookie 消失了。所以,当 COEP 的值为 credentialless时,跨域资源不会携带任何 cookie。
跨域隔离(Cross-Origin Isolation)
跨域隔离是一种 Web 安全策略,它通过严格控制不同来源资源之间的访问,创建一个安全隔离的执行环境。这种隔离机制主要用于确保 Web 应用能够安全地访问一些高敏感性或高精度的 API。
- 只有启用了跨域隔离,才能使用
SharedArrayBufferAPI(用于共享内存)。
- 只有启用了跨域隔离,才能使用
performance.measureUserAgentSpecificMemory()API(用于评估 web 应用的内存使用,包含其内部的所有 iframes 和 workers)。
- 为增强对时间攻击和指纹识别的防护,浏览器会根据站点的隔离状态调整
performance.now()的时间精度,当启用了跨域隔离,performance.now 的精度会更高:
-
- 在隔离上下文中(启用了跨域隔离),performance.now() 的精度偏差设为 5 微秒(0.005 毫秒)。因为隔离上下文限制了对共享资源的访问,这种更高精度的计时在这里更安全,可以满足性能敏感应用的需求,有助于减少跨站攻击的风险。
-
- 在非隔离上下文中(未启用跨域隔离),精度偏差则被限制为 100 微秒(0.1 毫秒)。这种较低的精度有助于减轻时间攻击和指纹识别的风险,因为它使得脚本更难检测到处理时间的微小变化,从而降低跨域时间侧信道攻击的有效性。
作用
- 浏览器会对其提供更高级别的 API 支持(如 SharedArrayBuffer 等),同时确保隔离性和安全性。
- 同时跨域隔离可以减少跨站点脚本(XSS)和跨站请求伪造(CSRF)等攻击的风险,保护敏感数据不被不受信任的资源访问。
- 跨域隔离还可以限制某些容易被
Spectre和Meltdown等侧信道攻击利用(Side-Channel Attack,是一类通过观察和分析计算机系统的物理特性或外部行为来推断系统内部数据的攻击方法。这类攻击并不是通过传统的软件漏洞,而是利用系统的时间、能耗、声音、电磁辐射等非直接信息来间接推测数据内容)的资源访问,使得页面更难以利用这些漏洞获取敏感数据。
如何设置
要使 Web 应用启用跨域隔离环境,需要使用到上面我们学习过的两个 HTTP 响应头:
- Cross-Origin Embedder Policy (COEP): 设置为
require-corp或者credentialless。
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Embedder-Policy: credentialless
- Cross-Origin Opener Policy (COOP):设置为
same-origin。
Cross-Origin-Opener-Policy: same-origin
当满足以上两个条件时,页面就可以进入跨域隔离环境,通过 crossOriginIsolated 属性可以返回当前是否是跨域隔离状态:
const myWorker = new Worker("worker.js");
if (crossOriginIsolated) {
const buffer = new SharedArrayBuffer(16);
myWorker.postMessage(buffer);
} else {
const buffer = new ArrayBuffer(16);
myWorker.postMessage(buffer);
}
结束语
这一章我们学习了源 Origin 的相关内容,扩展了一下 site 的知识,对于理解例如 cookie 中的 sameSite 条件是很有帮助的。还扩展了 document.domain 属性,可以用于放宽同源策略的限制,当然这个属性已经不建议使用了,大家了解一下这个属性以及它的替代方案即可。
又提到了以前我们学习过的跨域开放者策略,这对于新打开的页面是否能获取到源页面的信息非常重要,例如有一些做监控的工具可能需要用到该信息。并且还会影响与源页面的通信。
我们还学习了跨域嵌入策略,对于如何更好的控制我们页面嵌入的跨域资源,有了更深刻的认识。
最后结合跨域开放者策略以及跨域嵌入策略,会有一个跨域隔离的概念,它对于提高应用的安全性和隔离性,具有非常重要的作用。
那么这一章的介绍就结束了,咱们下一章再见啦。
欢迎关注我的公众号,前端创可贴。