一文解读Cookie

什么是Cookie?

Cookie 就是访问者在访问网站后留下的一个信息片段。它存储在客户端(通常来说是浏览器)。你可以把cookie当作一个map,里边是键值对,每个键值对有过期时间路径脚本可否访问等描述信息;描述信息存储在客户端,客户端请求时,默认会带上cookie的名称和值,不会带描述信息,通过http请求报文header中的cookie项进行传输;服务器响应时,可以设置cookie信息,就在http响应报文的headerSet-Cookie项。

那么,它究竟有什么作用呢?

众所周知,HTTP 协议是无状态的协议,如果你在同一个客户端向服务器发送多次请求,服务器不会知道这些请求来自同一客户端(同一个用户)。

这样有什么好处呢?如果它是有状态协议,你必须要时刻与服务器建立链接,那么如果连接意外断开,整个会话就会丢失,重新连接之后一般需要从头开始;而如果是无状态协议,使得会话与连接本身独立起来,这样即使连接断开了,会话状态也不会受到严重伤害,保持会话也不需要保持连接本身。

但是,缺点也很明显:即使同一个客户端连续两次发送请求给服务器,服务器也识别不出这是同一个客户端发送的请求,这导致的问题就比如你加了一个商品到购物车中,但因为识别不出是同一个客户端,你刷新下页面就没有了。

为了使服务器知道每个请求具体来自于哪个用户,比如你在逛淘宝的时候你只需要登录一次,当你发起一次购买请求,服务器就已经知道你登录过了,不会再让你进行登录。

由此,Cookie诞生了,Cookie就是一种浏览器管理状态的一个文件,让无状态的 HTTP 协议拥有一小块记忆。

HTTP Cookie 机制是 HTTP 协议无状态的一种补充和改良

Cookie 主要有以下用途:

  • 会话管理:登陆、购物车等应该记住的其他内容
  • 个性化:用户偏好、主题或者其他设置

  • 追踪:记录和分析用户行为

Cookie原理

第一次访问网站的时候,浏览器发出请求,服务器响应请求后,会将cookie放入到响应请求中(就在http响应报文的headerSet-Cookie项),在浏览器第二次发请求的时候,会把cookie带过去(http请求报文header中的cookie项),服务端会辨别用户身份,当然服务器也可以修改cookie内容。

Set-Cookie 和 Cookie 标头

Set-Cookie HTTP 响应标头将 cookie 从服务器发送到用户代理。下面是一个发送 Cookie 的例子

在这里插入图片描述

随着对服务器的每个新请求,浏览器将使用 Cookie 头将所有以前存储的 Cookie 发送回服务器。

在这里插入图片描述

Cookie的分类

有两种类型的 Cookies,一种是 Session Cookies,一种是 Persistent Cookies,如果 Cookie 不包含到期日期,则将其视为会话 Cookie。会话 Cookie 存储在内存中,永远不会写入磁盘,当浏览器关闭时,此后 Cookie 将永久丢失。如果 Cookie 包含有效期 ,则将其视为持久性 Cookie。在到期指定的日期,Cookie 将从磁盘中删除。还有一种是 Cookie的 Secure 和 HttpOnly 标记,后面会介绍。

会话 Cookies

会话 Cookie 有个特征,客户端关闭时 Cookie 会删除,因为它没有指定Expires或 Max-Age 指令。

但是,Web 浏览器可能会使用会话还原,这会使大多数会话 Cookie 保持永久状态,就像从未关闭过浏览器一样。

例如:

在这里插入图片描述

永久性 Cookies

永久性 Cookie 不会在客户端关闭时过期,而是在特定日期(Expires)特定时间长度(Max-Age)外过期。例如:

在这里插入图片描述

name

cookie的名字,一个域名下绑定的cookiename不能相同,相同的name的值会被覆盖掉

value

value表示cookie的值

【注】用 JavaScript操作 Cookie 的时候注意对 value 进行编码处理。

Domain

这个代表的是,cookie绑定的域名,如果没有设置,就会自动绑定到执行语句的当前域。由于同源策略,脚本只能访问父域名或本域名的cookie(浏览器只能发送父域名或本域名的cookie),比如设置cookie域名为一级域名mydomain.com;那么此域名下的二级域名www.mydomain.comimages.mydomain.com页面,都可以访问此cookie

【注】cookie区分域,而不区分端口,也就是说,同一个ip下的多个端口下的cookie是共享的!更确切的说,请求是会带上同域下所有端口的cookie,但是返回时会覆盖。

例如以下栗子:

在这里插入图片描述
在这里插入图片描述

Path

Path这个属性默认是'/',这个值匹配的是web的路由

cookie是区分路径的,也就是说,同一个键值对可以同时设置到 www.mydomain.com,www.mydomain.com/b下,并且是独立的,互不影响的;如果不指定路径,默认是当前路径。

Domain 和 Path 标识共同定义了 Cookie 的作用域:即 Cookie 应该发送给哪些 URL

Expires

Expires用于设置 Cookie 的过期时间。当 Expires 属性缺失时,表示是会话性 Cookie,值保存在客户端内存中,并在用户关闭浏览器时失效。需要注意的是,有些浏览器提供了会话恢复功能,这种情况下即使关闭了浏览器,会话期 Cookie 也会被保留下来,就好像浏览器从来没有关闭一样。

与会话性 Cookie 相对的是持久性 Cookie,持久性 Cookies 会保存在用户的硬盘中,直至过期或者清除 Cookie。这里值得注意的是,设定的日期和时间只与客户端相关,而不是服务端。

所以如果你想要cookie存在一段时间,那么你可以通过设置Expires属性为未来的一个时间节点,Expires这个是代表当前时间的,然而这个属性已经逐渐被Max-Age代替。

Max-Age

Max-Age 用于设置在 Cookie 失效之前需要经过的秒数。

Max-Age可以为正数、负数、甚至是 0。

  • 当 Max-Age 属性为正数时,浏览器会将其持久化,即写到对应的 Cookie 文件中。

  • 当 Max-Age 属性为负数,则表示该 Cookie 只是一个会话性 Cookie

  • 当 Max-Age 为 0 时,则会立即删除这个Cookie。因为cookie机制本身没有设置删除cookie,失效的cookie会被浏览器自动从内存中删除,所以,它实现的就是让cookie失效。

【注】假如 Expires 和 Max-Age 都存在,Max-Age 优先级更高。

Secure

由于http不仅是无状态的,还是不安全的协议,容易被劫持。所以标记为 Secure 的 Cookie 只应通过被https协议加密过的请求发送给服务端。使用 https 安全协议,可以保护 Cookie 在浏览器和 Web 服务器间的传输过程中不被窃取和篡改。

HTTPOnly

如果这个属性设置为true,就不能通过js脚本来获取cookie的值,能有效的防止xss攻击。

SameSite

Chrome 51 开始,浏览器的 Cookie 新增加了一个SameSite属性,用来防止 CSRF 攻击和用户追踪。

Cookie 的SameSite属性可以设置三个值:

  • Strict
  • Lax
  • None

Strict

Strict最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie

这个规则过于严格,可能造成非常不好的用户体验。比如,当前网页有一个 GitHub 链接,用户点击跳转就不会带有 GitHub 的 Cookie,跳转过去总是未登陆状态。

Lax

Lax规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。

导航到目标网址的 GET 请求,只包括三种情况:链接,预加载请求,GET 表单。详见下表。

请求类型示例正常情况Lax
链接 <a href="..."></a> 发送 Cookie 发送 Cookie
预加载 <link rel="prerender" href="..."/> 发送 Cookie 发送 Cookie
GET 表单 <form method="GET" action="..."> 发送 Cookie 发送 Cookie
POST 表单 <form method="POST" action="..."> 发送 Cookie 不发送
iframe <iframe src="..."></iframe> 发送 Cookie 不发送
AJAX $.get("...") 发送 Cookie 不发送
Image <img src="..."> 发送 Cookie 不发送

设置了StrictLax以后,基本就杜绝了 CSRF 攻击。当然,前提是用户浏览器支持 SameSite 属性。

None

Chrome 已将Lax变为默认设置。这时,网站可以选择显式关闭SameSite属性,将其设为None。不过,前提是必须同时设置Secure属性(Cookie只能通过 https 协议发送),否则无效。

Cookie的读写

document.cookie属性用于读写当前网页的 Cookie

读取的时候,它会返回当前网页的所有 Cookie,前提是该 Cookie 不能有HTTPOnly属性。

document.cookie // "foo=bar;baz=bar"

document.cookie属性是可写的,可以通过它为当前网站添加 Cookie
写入的时候,Cookie的值必须写成key=value的形式。注意,等号两边不能有空格。另外,写入 Cookie 的时候,必须对分号、逗号和空格进行转义(它们都不允许作为 Cookie 的值),这可以用encodeURIComponent方法达到。
但是,document.cookie一次只能写入一个 Cookie,而且写入并不是覆盖,而是添加。

document.cookie = 'test1=hello'; document.cookie = 'test2=world'; document.cookie // test1=hello;test2=world

document.cookie读写行为的差异(一次可以读出全部 Cookie,但是只能写入一个 Cookie),与 HTTP 协议的 Cookie 通信格式有关。浏览器向服务器发送 Cookie 的时候,Cookie字段是使用一行将所有 Cookie 全部发送;服务器向浏览器设置 Cookie 的时候,Set-Cookie字段是一行设置一个 Cookie

写入 Cookie 的时候,可以一起写入 Cookie 的属性。

document.cookie = "test=hello; expires=Fri, 31 Dec 2020 23:59:59 GMT";

上面代码中,写入 Cookie 的时候,同时设置了expires属性。属性值的等号两边,也是不能有空格的

各个属性的写入注意点如下。

  • path属性必须为绝对路径,默认为当前路径。
  • domain属性值必须是当前发送 Cookie 的域名的一部分。比如,当前域名是example.com,就不能将其设为foo.com。该属性默认为当前的一级域名(不含二级域名)。
  • max-age属性的值为秒数。
  • expires属性的值为 UTC 格式,可以使用Date.prototype.toUTCString()进行日期格式转换。

document.cookie写入 Cookie 的例子如下。

document.cookie = 'fontSize=14; ' + 'expires=' + someDate.toGMTString() + '; ' + 'path=/subdirectory; ' + 'domain=*.example.com';

Cookie 的属性一旦设置完成,就没有办法读取这些属性的值。

删除一个现存 Cookie的唯一方法,是设置它的expires属性为一个过去的日期。或者上面提到的Max-Age属性。

document.cookie = 'fontSize=;expires=Thu, 01-Jan-1970 00:00:01 GMT';

上面代码中,名为fontSize的 Cookie 的值为空,过期时间设为1970年1月1月零点,就等同于删除了这个 Cookie

posted @ 2021-12-07 08:54  智慧园区-老朱  阅读(1041)  评论(0编辑  收藏  举报