1、cookie是什么
科学百科中给的定义是:cookie是在http协议下,服务器或脚本可以维护客户工作站上信息的一种方式。cookie是由Web服务器保存在用户浏览器上的小文本文件,它可以包含有关用户的信息。无论何时用户链接到服务器,Web站点都可以访问cookie信息。
简单来说就是存储在浏览器中的纯文本,浏览器的安装目录下会专门有个cookie文件夹存放各个域下的cookie。
它是服务器发送到用户浏览器并保存在本地的一小块数据,当浏览器下次向同一服务器再发起请求时被携带并发送到服务器上,体现在自动添加到request header中的cookie字段里面。这一过程是浏览器自动帮我们做的,并且是每次请求浏览器都会自动做的事情。基于这个特点,cookie中存放的数据就有一定的要求,比如必须是每个请求都会发送给服务端的数据(如身份验证信息),否则的话浏览器自动设置添加这些cookie无疑会增加网络开销,并不合适。

cookie标准是有限制的:
-
每个域名下的cookie的大小最大为4KB,超出会被截掉并且不会被发送至服务器;
-
每个域名下的cookie数量最多为20个(但现在很多浏览器还是支持大于20个的)。
注:原始规范中限定每个域名下不超过 20 个 cookie,早期的浏览器都遵循该规范,并且在 IE7 中有更近一步的提升。在微软的一次更新中,他们在 IE7 中增加 cookie 的限制数量到 50 个,与此同时 Opera 、Safari、Firefox 和 Chrome 对与每个域名下的 cookie 个数没有限制(opera本来是由30个数量限制的,后转用webkit内核,无限制了)。
这些限制主要在阻止滥用cookie,数量过多的话会严重影响请求的性能。
1)存储cookie
cookie可以由服务端通过Set-Cookie方法设置,也可以通过js的 document.cookie = “name=value”设置。
-
浏览器端 通过document.cookie设置cookie,每次只能设置一个值,设置多个后面的无效;
-
document.cookie设置值可以不用遵守name=value的形式,直接设置一个字符串也是可以存储成功的;
-
服务端,通过response.setHeader中的set-cookie设置, 同样不能将多个cookie放在一个set-cookie字段中。


服务端同样可以设置可选项。
2)cookie编码
a.值不能包含空格,括号,括号,等号,逗号,双引号,斜杠,问号,符号,冒号和分号。
b.使用 unicode 字符时需要对 unicode 字符进行编码,否则会乱码(中文属于 unicode 字符,在内存中占4个字符,而英文属于ASCII字符,内存中只占2个字节)。
c.cookie 不仅可以使用 ASCII 字符与 unicode 字符,还可以使用二进制数据。例如在 cookie 中使用数字证书,提供安全度。使用二进制数据时也需要进行编码。
3)js获取cookie的API
通过document.cookie方法可以获取cookie,但是注意,这种方式只能获取到非HttpOnly类型的cookie。

4)cookie的可选项
cookie共有domain、path、expires(max-age)、httpOnly、secure这几种。每设置一个cookie,都会包含这些信息,如果没有设置这些属性,则会使用这些属性的默认值,如:
1 | |
指定的可选项只能在浏览器中查看参考,并不会被带到服务端。
a.expires/max-age
这个可选项代表cookie什么时候过期,过期了这个cookie也就不存在了。它的格式必须是GMT的时间(new Date().toGMTString()或者 new Date().toUTCString())。
- 如果没有设置该可选项,那么这个cookie在浏览器关闭后就冇(mao 三声,意思是没有)啦。
(ps:别问为什么一个东北人要给你们科普粤语,问就是剧情需要)
- 这个可选项是http/1.0协议的概念,http/1.1用max-age代替,两者作用一样,只是max-age的格式是以秒为单位的数字,并且如果设置为-1,则代表永远失效;如果设置为0,代表删除cookie。
b.domain和path
domain指定了 cookie 将要被发送至哪个或哪些域中,path代表要发送的具体路径,两者加起来就组成了个完整的URL,domain和path一起来限制cookie能被哪些URL访问,也就证明了不同域名的cookie是不能共享的(二级域名除外)。核实过domain后才会对path进行比较。
也就是说满足以上两个条件(domain可以是一级也可以是二级域名)的url,都会在请求头中设置浏览器中的cookie。
在没有设置这两个可选项的情况下,则会使用默认值。domain的默认值为设置该cookie的网页所在的域名,path默认值为设置该cookie的网页所在的目录。
但是要注意两点:
-
发生跨域xhr请求时,即使请求URL的域名和路径都满足cookie的domain和path,默认情况下cookie也不会自动被添加到请求头部中。
-
domain是可以设置为页面本身的域名(本域),或页面本身域名的父域,但不能是公共后缀(com、net、cn、cc等等)。举例说明下:如果页面域名为 www.baidu.com, domain可以设置为“www.baidu.com”,也可以设置为“baidu.com”,但不能设置为“.com”或“com”。
-
cookie不区分端口,假设在127.0.0.1:8000设置了某些cookie,然后修改端口,重新发送请求,还是可以拷打request header中带上了 之前不同端口时设置的cookie字段。
-
domain不能设置成当前域名之外的域名(不管是精确的还是非精确的)。
主要原因是:浏览器在判断是否是第三方(跨域)请求时,用的判断逻辑不是同源策略,而是用了 Public Suffix List 来判断。举个例子:foo.a.com 和 bar.a.com 是不同源的,但很有可能是同一个站点的,a.com 和 a.com:8000 是不同源的,但它俩绝对是属于同一个站点的。
c.secure
这个可选项代表设置的cookie只在安全环境中发送,也就是说,在https或其他安全协议中,包含secure选项的cookie才能被发送到服务器,防止信息在传递的过程中造成信息泄漏。
默认情况下,服务端设置cookie时不会带secure选项。所以默认情况下,不管是HTTPS协议还是HTTP协议的请求,cookie 都会被发送至服务端。被发送到服务端后,在 HTTPS 链接上传输的 cookie 都会在服务端被添加上 secure 选项(一般有框架自动做这个事情)。
注:在http协议的网页中是无法设置secure类型cookie的;且在服务端也是只有https的情况下才能写入secure的cookie。
d.httpOnly
这个可选项用来设置cookie是否能通过js去访问。在客户端是不能通过js代码去设置一个httpOnly类型的cookie的,这种类型的cookie只能通过服务端来设置。
这样做是为了保障安全。如果任何cookie都能被客户端通过document.cookie获取的话,假设有一段恶意的script脚本插到了网页中,这段script脚本通过document.cookie读取了用户身份验证相关的cookie,并将这些cookie发送到了攻击者的服务器。攻击者轻而易举就拿到了用户身份验证信息,一点安全性都没有。–>
1 | |
当用户点击这个链接的时候,浏览器就会执行onclick里面的代码,结果这个网站用户的cookie信息就会被发送到abc.com攻击者的服务器。攻击者同样可以拿cookie搞事情。
e.SameSite
cookie一开始被设计成了允许第三方网站共享的,但是这样很容易引起CSRF攻击。为了防止CSRF攻击,已经有的办法是CSRF token校验和referer请求头校验。但为了从根源上解决这个问题,为 Set-Cookie 响应头新增 SameSite 属性,它有以下三个可选值:
- Strict:表示严格模式,表明这个cookie在任何情况下都不可能作为第三方cookie。
1 | |
在A域名下发起一个域名为B的任意请求,这时 cookie a 不会被发送出去,也就是不会被包含到请求头中,但 cookie b 会。
- Lax:宽松模式,比strict放宽了点限制。
1 | |
在A域名下点击进入一个域名为B的链接,这时 cookie a 不会被包含到请求头中,但 cookie b、 cookie c 会;但假如在A域名下发起一个域名为B的请求(script、link、img、iframe 等标签发起的请求,还有通过各种发送 HTTP 请求的 DOM API(XHR,fetch,sendBeacon)发起的请求),不会带上 cookie b。
2、修改和删除cookie
-
修改一个cookie只需要重新赋值。但要注意在设置新cookie时,name/path/domain这几个选项一定要与旧的cookie保持一样。否则不会修改旧值,而是添加了一个新的 cookie。
-
删除一个cookie可以将这个新cookie的expires选项设置为一个过去的时间点。但同样要注意,name/path/domain/这几个选项一定要与旧的cookie 保持一样。
自动删除cookie的条件:
-
未设置expires的cookie的会话被关闭。
-
设置了expires的cookie达到失效日期被删除。
-
浏览器cookie达到上限,为了给新建cookie腾出空间,会删除原有的cookie。(但实际上cookie在现代浏览器中没有个数限制了,所以此条可以忽略)
3、如何解决cookie跨域问题
1)host绑定
2) 用nginx的反向代理解决
比如localhost:8080不具备的cookie,可以通过为其设置proxy_pass和proxy_set_header代理到线上已有的域名上,
1 | |
proxy_set_header Cookie $http_cookie;
以上两种方法原理相同,都是通过代理的方式,让原本不具备某些cookie的页面能共享到有效cookie,从而能正确发起请求。
问题:
Q1:xhr跨域请求 set-cookie可以被删除吗?
A1:a.com向b.com发送请求,分以下几种情况,第一种,b.com的服务端设置的cookie 域名是a.com,此时,在浏览器中可以直接访问a.com的页面删除此cookie;若b.com的服务端设置的cookie 域名是b.com,此时,在a.com中看不到此cookie,无法从浏览器端删除,只能再次发送某个请求给b.com进行删除。
Q2:某个cookie的domain是sso.dasouche-inc.net 那么xx.sso.dasouche-inc.net 可以访问这个域名的cookie 吗
A2:不可以的,除非这个cookie的domain是.sso.dasouche-inc.net。
Q3:有没有什么办法能够让cookie只被.dasouche-inc.net访问 而不被精准的域名下的路径访问
A3:暂时没有想到通过修改request headers来实现此场景。
参考文档: