前言
今天来了解一下什么是跨站、什么是跨域、什么是同站。在这些不同的场景下cookie、session、localStorage...缓存如何表现、如何禁止三方cookie等
。那么接下来将解析一下涉及到的知识点,篇幅可能有点长,希望对你有所帮助
域名构成
首先来了解一下域名的构成
。域名由多个部分组成、阅读顺序从右到左
每一部分都提供特定的信息
域名构成: 协议(schema)
+子域名...(subdomain)
+二级域名(second-level domain)
+顶级域名(Top-level domain)
+目录...(subdirectory)
+参数(params)
-
子目录(subdirectory)
子目录可以帮助用户和网络爬虫了解到位于网站的那个特定部分
例如: tencent公司提供了游戏
、AI大模型
、云服务
等。当我访问www.tencent.com/aigc
时,子目录为aigc
意味着其提供了aigc
相关页面,在这个页面用户就可以了解到关于AI大模型、生成模型
的推广 -
TLD (Top-Level Domain,顶级域名)
顶级域名
指定您的组织在互联网上注册为哪种类型的实体
例如:.cn
表示中国的商业实体
或者.com适用于美国或者全球商业实体
,.edu
用于教育或研究机构
,.org
用于某些组织的机构
等等。 顶级域名由这里可以查看具体数据 -
SLD (second-level domain 二级域名)
紧挨着TLD
前面标签
就被称为二级域名(SLD)
。一个域名可以有多个标签
没有强制规定3
标签(存在四级、五级...域名)。
二级域名SLD
是网站的名称,用于告诉用户当前正在访问那个品牌、公司...
的网站
例如:baidu.com
就知道访问的是百度公司
旗下的网站、tencent.com
就知道访问的是腾讯公司
旗下的网站 -
子域名(subdomain)
二级域名就像一栋大厦
,那么子域名
就像大厦里面的房间号
。子域名理解某个公司给你提供的具体服务
例如: 一般情况下公司官网域名为www.baidu.com
、云服务的网站域名为cloud.baidu.com
、api接口服务器的域名为api.baidu.com
等。通过查看子域名可以知道具体的内容服务类型
-
协议(schema)
用于告诉网络服务器访问网站上的页面时所采用的是哪种协议
例如: HTTPS(即超文本传输安全协议)、ws(websocket长链接协议)、mailto:// (电子邮箱协议)
同源策略(SOP)
在web浏览器中,如果两个URL的协议
、域名
、端口
保持一致,则这个两个URL属于同源
否则属于非同源即为跨域
。可以防止脚本和其他web资源跨不同源来访问和操作数据。
接下来以http://www.aklcown.com
链接为例子:
- 协议:http
- 域名:www.aklcown.com
- 端口:443 (https默认443,http默认80)
URL | 是否同源 | 理由 |
---|---|---|
http://www.aklcown.com/index.html | ✅ | 只有路径不同 |
https://www.aklcown.com | ❌ | 协议不同 |
http://www.aklcown.com:81 | ❌ | 端口不同(http:// 默认端口是 80) |
http://www.ak.com | ❌ | 域名不同 |
同源策略限制
XMLHttpRequest
: 禁止使用XHM发送不同源服务器HTTP请求,即不能发送跨域ajax请求(防止CSRF攻击
)本地缓存
: cookie、localStorage、IndexDB受到同源策略的限制
DOM操作
: iframe父子窗口不是同源
的话,在访问彼此的DOM
、读取数据
收到同源显示无法访问
子域名限制
:SOP将子域名视为单独的源
因此除非建立跨域权限
否则父子域之间是无法共享资源的
(sub.aklcown.com无法访问aklcown.com的资源)
PS: 可以通过一些手段进行规避。例如iframe的postmessage、跨域权限设置document.domain、服务端配置Access-Control-Allow-Origin..
为什么需要同源策略
同源策略是 Web 浏览器实施的重要安全机制,用于保护用户数据并防止未经授权的访问
接下来看一下前端经典面试最常见的CSRF
和XSS
安全问题
跨站点请求防伪(CSRF)
: CSRF攻击诱导用户在web程序上执行非预期的操作。
案例: 一个小破站有一个弹窗,你不小心点击了弹窗,弹窗可能会执行招商银行转账的请求
。在这之前你使用web浏览器
登录过招商银行
那么此时你本地的浏览器保存了招商身份认证信息的cookie
。那么当cookie不受限制与同源策略
的话,弹窗执行网络请求
也将携带身份认证信息的cookie
从而导致招商系统认定为你是本人操作
跨站点脚本攻击(XSS)
: 当黑客将恶意脚本注入到受信任的网站时就会发生XSS攻击,从而窃取用户信息
或者篡改网页
内容。(iframe就存在同源限制,对于非同源父子窗口是没有办法捕获到彼此的dom事件...。可以通过postmessage来通信)
跨域
非同源
即为跨域
跨域常见的场景有如下两种:
http网络请求跨域
浏览器会默认检测请求是否跨域
,具体跨域原理
这里不再阐述可以看这篇文章CORS跨域原理解析
当我在
NestJs服务开启app.enableCors();
运行跨域时,响应体存在Access-Control-Allow-Origin
浏览器判定允许跨域请求iframe跨域
创建3个html文件分别为1.html
、2.html
、3.html
。1.html
与3.html
不是同源情况、2.html
和3.html
属于同源情况
<iframe src="http://127.0.0.1:5500/3.html" frameborder="0"></iframe>
<script>
(function () {
const iframe = document.getElementsByTagName("iframe")[0];
iframe.onload = function () {
console.log(iframe.contentWindow.document.getElementById("ak"));
};
})();
</script>
1.html
是不允许获取到3.html
的dom元素
且浏览器会报错异常
。 我们可以通过postmessage
来进行1 <=> 3
之间的通信来实现我们需要的操作
2.html
是允许获取到3.html
的dom元素
PS: 不同源的iframe
也无法监听iframe的事件机制
,最近在做weboffice
其使用了iframe
做了沙箱
导致我在外层没办法监听里层的keypress
事件...
同站
同站
需要满足如下条件
- 域名之间具有相同的
SLD(二级域名)+TLD(有效顶级域名)
有效顶级域名
和顶级域名
是不一样的概念,例如github.io
是一个有效顶级域名,如果将.io
视为有效顶级域名,那么https://ziyi2.github.io
和https://xxholly32.github.io
将被浏览器视为同站,但显然它们是两个不同的开发者
创建的博客站点。有效顶级域名有一个维护列表,具体可以查看 publicsuffix/list
举个例子: http://www.aklcown.com/aigc
URL | 描述 | 是否同站 | 是否同源 |
---|---|---|---|
http://www.aklcown.com | SLD+TLD相同 | ✅ | ✅ |
http://www.aklcown.com:81 | SLD+TLD相同 | ✅ | ❌ |
http://sub.aklcown.com | SLD+TLD相同 | ✅ | ❌ |
https://www.aklcown.com | SLD+TLD相同 | ✅ | ❌ |
http://www.aklcown.top | TLD不同相同 | ❌ | ❌ |
http://www.aklcown1.com | SLD不同相同 | ❌ | ❌ |
跨站
非同站
即为跨站
。也就是两个域名之间SLD(二级域名)+TLD(顶级域名)
不一致
本地模拟环境
因为要查看缓存
在不同域名情况
下的表现,因此我们需要在本地模拟一个多域名
的测试环境。
那么如何在本地搭建多域名环境
呢?
- 首先下载SwitchHosts用于改变本地的
host
文件。
当我们在浏览器输入域名
时会先经过本地的host
文件来查看是否存在域名和ip地址映射
,因此当我们输入www.ak.com
实际访问的ip地址为127.0.0.1
。具体可以自行查找浏览器从输入到渲染所要经过的过程
- 分别创建
ak.html
、akclown.html
、sub.ak.html
对应不同域名访问
。
快速启动本地静态文件服务,可以采用Vscode默认的go live
。你也可以使用http-server
完成如上两步骤
我们就完成了模拟多域名环境搭建
,接下来就来看看web相关缓存的具体表现
缓存的表现
HTTP是属于无状态的
因此我们需要通过缓存的手段
来保持一些用户状态
。以下涉及到到的服务端
采用NestJS
构建
cookie
HTTP Cookie: (也叫 Web Cookie 或浏览器 Cookie)是服务器发送
到用户浏览器
并保存在本地的一小块数据。浏览器会存储 cookie 并在下次向同一服务器再发起请求时携带并发送到服务器上
。
这里我使用NestJs构建后端服务器
,通过Nest服务来设置cookie
前后端部署在同源
下:
当点击设置cookie
按钮之后/set-cookie
的响应头
会携带set-cookie
字段并且在浏览器上设置cookie
的键值对
/set-cookie的响应头
浏览器的cookie区域
/get-cookie的请求头,会自动携带
cookie
到后端
通过NEST设置基础的cookie,在同源
下运行一切正常。
前后端域名在同站
和跨站
下:
一般情况下前后端服务不可能部署到同一个域名、端口、协议
下的,毕竟前后端分离。如果还是按照上面基础的cookie设置
是没有办法给浏览器端设置到同站
和跨站
的cookie,
- 跨域是如何如何设置cookie
跨域cookie通过如下
两步骤设置
:- 前端: 如果是axios请求添加
withCredentials: true
参数,如何是fetch请求诶添加credentials:include
参数 - 后端: 配置
Access-Control-Allow-Origin
需要具体的域名不能是*
。配置access-control-allow-credentials
为true
- 前端: 如果是axios请求添加
app.enableCors({
origin: [
'http://www.ak.com:5500',
'http://sub.ak.com:5500',
'http://www.akclown.com:5500',
'http://api.ak.com:3000',
],
credentials: true,
});
-
知道了
如何设置跨域的cookie
之后,先来了解一下cookie相关的domain
和sameSite
关键字-
domain: 指定cookie可以送达的主机(
提供API服务的主机域名,不是客户端域名
)。其值只能设置当前域名
或者更高级别
的域名。设置域名将会使 cookie 对指定的域名
及其所有子域名
可用ps: 即使
sameSite: 'strict'
限制了 cookie 的发送范围,设置了domain
属性后,子域名仍然可以访问这个 cookie -
sameSite: 控制cookie是否
同站(一方cookie)
、跨站(三方cookie)
请求一起发送
-
值 | 描述 | 一方cookie(同站) | 三方cookie(跨站) |
---|---|---|---|
Strict | 浏览器仅对同一站点 发送cookie,即请求来自设置 cookie 的站点 | ✅ | ❌ |
Lax (默认值) | 浏览器对符合同站域名 发送cookie | ✅ | 部分会携带cookie |
None | 浏览器在跨站 和同站 请求中均会发送cookie,必须设置 Secure | ✅ | ✅ |
domain的设置是针对服务器API地址的、而sameSite的设置是针对同站(一方cookie)
和跨站(三方cookie)
- 下面来看看
同站
的情况,先新增一个api.ak.com
域名作为后端服务的域名
- 首先来看一下如下:
设置domain的五种情况
在
http://www.ak.com/
域名下调用set-cookie
请求, 可以从response header
看出cookie
的domain
只能设置为当前域名
或更高级别的域名
接下来看看
当前api域名
和当前api的子域名
携带cookie的情况
- 在
http://www.ak.com/
发送api.ak.com/get-cookie
请求,可以看到username\age\id
都会携带上 - 在
http://www.ak.com/
发送sub.api.ak.com/hello
请求,可以看到age\id
会携带上,而username
没有被携带上,这就很好的证明了设置了domian对指定的域名及其所有子域名可用
- 在
http://sub.ak.com/
发送api.ak.com/get-cookie
请求,可以看到username\age\id
都会携带上(浏览器默认行为,也许同站是被共享cookie作用域的
目前没查到相关资料,有了解的大佬指点一下,thanks)
设置sameSite三种情况
因为sameSite值为node
时需要同步设置secure
为true,而secure只在https
中才能生效。因此我们先来使用NestJs模拟一个HTTPS环境
- 首先来看一下如下:
生成SSL证书
1. 生成私钥:
openssl genrsa -out key.pem 2048
2. 生成证书签名请求(CSR):
openssl req -new -key key.pem -out csr.pem
3. 生成自签名证书:
openssl x509 -req -days 365 -in csr.pem -signkey key.pem -out cert.pem
Nest服务中:
const httpsOptions = {
key: fs.readFileSync(path.join(__dirname, '../certificate/key.pem')),
cert: fs.readFileSync(path.join(__dirname, '../certificate/cert.pem')),
};
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
httpsOptions,
});
通过如上配置,然后在浏览器中访问https://api.ak.com:3000/hello
浏览器会警告你这是一个不受信任的自签名证书,选择忽略警告即可。
接口set-same-site-cookie
在https://www.ak.com:5500/
触发设置cookie。接口set-other-cookie
在https://www.akclown.com:5500/
触发设置cookie
经过上述配置,可以在控制面板下看到如下cookie:
当我在
https://www.akclown.com:5500
域名下请求https://api.ak.com:3000
接口(跨站了
)。可以看到设置cookie属性为{sameSite: 'none', secure: true}
的id会被携带上,其余username
和age
不会被携带到www.akclown.com
服务上
浏览器需要开启
三方cookie
总结: domain作用于同站(接口域名与客户端域名相同的SLD+TLD+schema)
的情况下, sameSite作用于跨站(接口域名与客户端域名SLD+TLD+schema有一个不满足)
的情况下。
session
使用Nest搭建session
环境,可以从如下配置看到session
可以配置cookie
属性。session的具体在客户端是否携带对应的session cookie
取决于这个cookie
配置,表现跟cookie章节一样
客户端设置与获取
总结: session在同站
、跨站
如何表现,取决于NestJS在设置的session时传入的cookie
参数。其值的表现跟cookie章节一致
补充: 当接口域名
和前端域名
跨站时,只有samesite:none
才能设置成功
localStorage
如下图可以看出localStorage
受到同源策略
的限制的,即使是(同站)子域名
也不可以访问
sessionStorage
sessionStorage
与localStorage
一样都是受到同源策略
的限制。只能同源
才可以访问数据
三方cookie
有时候我们会很好奇,为什么在抖音、小红书
看了某些商品之后打开京东、淘宝
就会给你推荐对应的商品
?
原因就是三方cookie搞的鬼
, 那么什么是一方cookie
、什么是三方cookie
呢?
举个例子:
以淘宝为例, 当我们访问https://www.taobao.com/
网站时会请求大量https://h5api.m.taobao.com/...
接口其通过set-cookie
设置大量的cookie
。因为请求域
和当前网站域名
是同站的,所以这种cookie属于一方cookie
但网站不一定只是调用
同站的域名
。例如www.taobao.com
网站就调用了很多www.tmall.com
域名下的接口,这些跨站的域名
接口也通过set-cookie
设置大量的cookie
。因为请求域
和当前网站域名
是跨站的,所以这种cookie属于三方cookie
上面只是涉及到
如何写入三方cookie
, 那么具体是如何通过三方cookie
进行用户信息采集呢? 这里有所解释,不再阐述
关于三方cookie
的更多内容点击这里查阅
跨域共享实现SSO
TODO:施工中...
总结
本文涉及的Demo源码
参考文献:
CORS跨域原理解析
跨域是如何如何设置cookie
HTTP Cookie 的运行机制
Analyzing Impact of WWW Subdomain on Cookie Security
附带身份凭证的请求
CORS protocol and credentials
Cookie策略真是越来越复杂了
什么是域名
HTTP cookie
浏览器专题系列 - 跨域与跨站
Parts of a URL: A Short Guide
Understanding SameSite cookies
快速了解第三方 Cookie 是如何跟踪你的行为