概念
HTTP Cookie(也叫Web Cookie或者浏览器Cookie)是服务端发送到用户浏览器保存在本地的一小块数据。浏览器会在下一次向同一个服务器再发起请求时携带并发送到服务器上。我们都知道HTTP(HTTP1.x)是一个无状态的协议(在同一个连接中,两个执行成功的请求之间是没有关系的,这就引发了一个问题,用户没有办法在同一个网站中进行连续的交互,比如在一个电商网站里,用户把某个商品加入到购物车,切换一个页面后再次添加了商品,这两次添加商品的请求之间没有关联,浏览器无法知道用户最终选择了哪些商品),而HTTP Cookie的到来就解决了这样一个问题,我们只需要把Cookie添加到请求头中,然后每次请求都携带上,就能共享信息了。
刚刚有提到Cookie还会保存在本地,我们一般是在浏览器中的Application-Cookie中查看cookie,但是有时Cookie的内容还可能会保存在设备本地例如MAC里的Chrome浏览器就把它保存在了~/Library/Application Support/Google/Chrome/Default中名为Cookie的文件。
Cookie限制
作用范围限制
Cookie是和特定的域名绑定。意思是不同域之间Cookie是不互通的,同时Cookie会与请求一起发送到创建它的域,这样一定程度保证了Cookie中存储信息只对认可的接受者开放。
存储大小限制
因为Cookie是在前面也提到了,是存放在客户端的,为了防止被恶意运用,所以浏览器也对Cookie的大小做了限制。
- 不超过300个Cookie
- 每个Cookie不超过4096字节
- 每个域不超过20个Cookie
- 每个域不超过81920字节
如果Cookie总数超过了单个域的上限,浏览器就会删除之前设置的Cookie。 IE和Opera会按照最近最少使用原则(LRU)去删除之前的Cookie,FirFox则会随机删除,所以最好最好以不要超过限制。如果创建的Cookie超过了最大字节限制,那么该Cookie会被静默删除。
Cookie结构
Name
唯一标识cookie的名称,cookie的名是不区分大小写了,所以test和Test是同一个名称。并且cookie的名必须经过URL编码。
Value
存储在cookie里的字符串的值,这个值会跟随请求发送到服务端,并且这个值也必须经过URL编码。
Domain
Cookie的有效作用域。发送到这个域的所有请求都会包含对应的cookie。这个值同时是可能包含子域的例如设置:.cookie.com,那么.a.cookie.com,.b.cookie.com都是有效的,如果不设置则会默认cookie为设置cookie的域。
Path
指定一个 URL 路径,这个路径必须出现在要请求的资源的路径中才可以发送 Cookie 标头。
字符 / 可以解释为文件目录分隔符,此目录的下级目录也满足匹配的条件(例如,如果 path=/docs,那么 /docs、/docs/、/docs/Web/ 和 /docs/Web/HTTP 都满足匹配条件,/、/docsets 或者 /fr/docs 则不满足匹配条件。
Expires/Max-Age
Expires: cookie 的最长有效时间,形式为符合 HTTP-date 规范的时间戳。如果没有设置这个属性,那么表示这是一个会话期 cookie。一个会话结束于客户端被关闭时,这意味着会话期 cookie 在彼时会被移除。
Max-Age: 在 cookie 失效之前需要经过的秒数。秒数为 0 或 -1 将会使 cookie 直接过期。假如 Expires 和 Max-Age 属性均存在,那么 Max-Age 的优先级更高。
Size
表示该条Cookie的大小。
HttpOnly
设置 HTTPOnly 属性可以防止客户端脚本通过 document.cookie 等方式访问 Cookie,有助于避免 XSS 攻击。
Secure
标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端。它永远不会使用不安全的 HTTP 发送(本地主机除外),这意味着中间人攻击者无法轻松访问它。
SameSite
这个属性在几年前几乎达到一个前端开发人员无人不知无人不晓的地步,就因为在Chrome 80版本的更新中默认屏蔽了第三方Cookie,导致非常多的前端页面的Cookie无法使用。
SameSite属性允许服务器指定是否/何时通过跨站点请求发送,防止入侵者通过针对跨站点请求伪造攻击(CSRF)来攻击网站。
SameSite主要有三种类型值:
- Strict: cookie仅发送它相同站点请求的,要求网页URL与请求目标URL完全一致
- Lex: 与Strict有些相似,只是在用户导航到cookie的源站点时发送cookie,允许部分第三方请求携带Cookie
- None: 指定浏览器会在同站请求和跨站请求下继续发送 cookie,但仅在安全的上下文中
那么问题就来了,在Chrome80之前默认是None,但更新后变更为了Lex。那么这个变更带来了哪些影响呢:
Cookie构建
class CookieUtil {
static get(name) {
let cookieName = `${encodeURIComponent(name)}=`,
cookieStart = document.cookie.indexOf(cookieName),
cookieValue = null;
if (cookieStart > -1) {
let cookieEnd = document.cookie.indexOf(";", cookieStart);
if (cookieEnd == -1) {
cookieEnd = document.cookie.length;
}
cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd));
}
return cookieValue;
}
static set(name, value, expires, path, domain, secure) {
let cookieText =
`${encodeURIComponent(name)}=${encodeURIComponent(value)}`
if (expires instanceof Date) {
cookieText += `; expires=${expires.toGMTString()}`;
}
if (path) {
cookieText += `; path=${path}`;
}
if (domain) {
cookieText += `; domain=${domain}`;
}
if (secure) {
cookieText += "; secure";
}
document.cookie = cookieText;
}
static unset(name, path, domain, secure) {
CookieUtil.set(name, "", new Date(0), path, domain, secure);
}
};
注意:因为所有 cookie 都会作为请求头部由浏览器发送给服务器,所以在 cookie 中保存大量信息可能会影 响特定域浏览器请求的性能。保存的 cookie 越大,请求完成的时间就越长。即使浏览器对 cookie 大小有 限制,最好还是尽可能只通过 cookie 保存必要信息,以避免性能问题。
参考资料:
《JavaScript高级程序设计》
浏览器系列之 Cookie 和 SameSite 属性
MDN