一、Cookie 是直接存储在浏览器中的一小串数据(一般不超过4kb)。它们是 HTTP 协议的一部分,由RFC 6265规范定义。
二、Cookie 通常是由 Web 服务器使用响应Set-Cookie HTTP-header 设置的。然后浏览器使用Cookie HTTP-header 将它们自动添加到(几乎)每个对相同域的请求中。
三、最常见的用处之一就是身份验证:
1、登录后,服务器在响应中使用Set-Cookie HTTP-header 来设置具有唯一“会话标识符(session identifier)”的 cookie。
2、下次如果请求是由相同域发起的,浏览器会使用Cookie HTTP-header 通过网络发送 cookie。
3、所以服务器知道是谁发起了请求。
四、我们还可以使用document.cookie属性从浏览器访问 cookie。

Cookie的查看

一、我们可以在浏览器的开发者工具中看到当前页面的Cookie
image.png
二、Cookie文件不只是存放在浏览器。Cookies相关的内容还可以存在本地文件里。

【示例】Mac下的Chrome,存放目录是~/Library/Application Support/Google/Chrome/Default,里面会有一个名为Cookies的数据库文件,可以用sqlite软件打开
image.png
1、存放在本地的好处就是即使关闭了浏览器,Cookie仍然可以生效。

Cookie的设置

一、 Cookie 是怎么设置的呢?简单来说就是

  1. 客户端发送 HTTP 请求到服务器
  2. 当服务器收到 HTTP 请求时,在响应头里面添加一个 Set-Cookie 字段
  3. 浏览器收到响应后保存下 Cookie
  4. 之后对该服务器每一次请求中都通过 Cookie 字段将 Cookie 信息发送给服务器。 | 【示例】以https://main.m.taobao.com/为例看一下这个过程
    1、在请求返回的Response Headers可以看到Set-Cookie字段
    image.png2、查看下Cookie
    image.png
    3、刷新页面,再看一下这个请求,可以在Request Headers看到cookie字段
    image.png | | —- |

读写Cookie

从 document.cookie 中读取

一、查看浏览器是否存储了本网站的任何 cookie

  1. // 在 javascript.info,我们使用谷歌分析来进行统计,
  2. // 所以应该存在一些 cookie
  3. alert( document.cookie ); // cookie1=value1; cookie2=value2;...

二、document.cookie的值由name=value对组成,以;分隔。每一个都是独立的 cookie。
1、为了找到一个特定的 cookie,我们可以以;作为分隔,将document.cookie分开,然后找到对应的名字。我们(1)可以使用正则表达式或者数组函数来实现。
三、document.cookie提供了对 cookie 的访问
1、写入操作只会修改其中提到的 cookie。
2、name/value 必须被编码。
3、一个 cookie 最大为 4KB,每个网站最多有 20+ 个左右的 cookie(具体取决于浏览器)。

写入 document.cookie

一、我们可以写入document.cookie。但这不是一个数据属性,它是一个访问器(getter/setter)。对其的赋值操作会被特殊处理。
二、对document.cookie的写入操作只会更新其中提到的 cookie,而不会涉及其他 cookie。

| 【示例】此调用设置了一个名称为user且值为John的 cookie:```javascript document.cookie = “user=John”; // 只会更新名称为 user 的 cookie alert(document.cookie); // 展示所有 cookie

  1. 1、如果你运行了上面这段代码,你会看到多个 cookie。这是因为document.cookie=操作不是重写整所有 cookie。它只设置代码中提到的 cookie user |
  2. | --- |
  3. 三、从技术上讲,cookie 的名称和值可以是任何字符,为了保持有效的格式,它们应该使用内建的encodeURIComponent函数对其进行转义:
  4. | 【示例】```javascript
  5. // 特殊字符(空格),需要编码
  6. let name = "my name";
  7. let value = "John Smith"
  8. // 将 cookie 编码为 my%20name=John%20Smith
  9. document.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value);
  10. alert(document.cookie); // ...; my%20name=John%20Smith

| | —- |

四、存在一些限制:
1、encodeURIComponent编码后的name=value对,大小不能超过 4KB。因此,我们不能在一个 cookie 中保存大的东西。
2、每个域的 cookie 总数不得超过 20+ 左右,具体限制取决于浏览器。
五、选项被列在key=value之后,以;分隔,像这样:

document.cookie = "user=John; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT"

Set-Cookie

一、Set-Cookie是属于响应头中的一个字段,它包含以下的值

  • NAME=VALUE: Cookie的名称和值。
  • domain=域名: 作为Cookie适用对象的域名,如果没有设置,默认是创建Cookie的服务器的域名。
  • path=PATH: 把服务器上的文件目录作为Cookie的使用对象,如果没有设置,默认是文档所在的文件目录。
  • expires=DATE: Cookie的有效期。
  • Secure: 只有在 HTTPS 时才会发送Cookie。
  • HttpOnly: JavaScript 不能访问Cookie。主要是为了防止跨站脚本攻击时Cookie的信息窃取。

二、这些值组成了浏览器中的Cookie选项/ Cookie属性

Cookie选项 / Cookie属性

【示例】
image.png

Name = Value

一、用 JavaScript 操作 Cookie 的时候注意对 Value 进行编码处理。

Domain

一、Domain 指定了 Cookie 可以送达的主机名。

domain=site.com

1、假如没有指定,那么默认值为当前文档访问地址中的主机部分(但是不包含子域名)。

【示例】淘宝首页设置的 Domain 就是 .taobao.com,这样无论是 a.taobao.com 还是 b.taobao.com 都可以使用 Cookie。

二、不能跨域设置 Cookie

| 【示例】阿里域名下的页面把 Domain 设置成百度是无效的:```javascript Set-Cookie: qwerty=219ffwef9w0f; Domain=baidu.com; Path=/; Expires=Wed, 30 Aug 2020 00:00:00 GMT

 |
| --- |

三、默认情况下,cookie 只有在设置的域下才能被访问到。<br />1、如果 cookie 设置在site.com下,我们在other.com下就无法获取它。<br />(1)我们在子域forum.site.com下也无法获取它

| 【示例】```javascript
// 在 site.com
document.cookie = "user=John"

// 在 forum.site.com
alert(document.cookie); // 没有 user

(2)无法使 cookie 可以被从另一个二级域访问,因此,other.com将永远不会收到设置在site.com的 cookie。 | | —- |

2、这是一项安全限制,为了允许我们可以将敏感信息保存在 cookie 中。
四、domain选项允许设置一个可以在子域访问的 cookie。
1、如果我们想要批准像forum.site.com这样的子域访问 cookie,这是可以做到的。当我们设置一个在site.com的 cookie 时,我们应该将domain选项显式地设置为根域:domain=site.com:

| 【示例】```javascript // 在 site.com // 使 cookie 可以被在任何子域 *.site.com 访问: document.cookie = “user=John; domain=site.com”

// 之后

// 在 forum.site.com alert(document.cookie); // 有 cookie user=John

 |
| --- |

(1) 出于历史原因,domain=.site.com(site.com前面有一个点符号)也以相同的方式工作,允许从子域访问 cookie。这是一个旧的表示法,如果我们需要支持非常旧的浏览器,则应该使用它。
<a name="6eOUa"></a>
## Path
一、Path 指定了一个 URL 路径。
```javascript
path=/mypath

1、必须是绝对路径。默认为当前路径。
2、这个路径必须出现在要请求的资源的路径中才可以发送 Cookie 首部。

【示例】设置Path=/docs,/docs/Web/下的资源会带 Cookie 首部,/test则不会携带 Cookie 首部。

3、该url 路径下的页面可以访问该 cookie。
二、通常,我们应该将path设置为根目录:path=/,以使 cookie 对此网站的所有页面可见。
三、Domain 和 Path 标识共同定义了 Cookie 的作用域:即 Cookie 应该发送给哪些 URL。

过期时间:Expires,Max-Age,

一、默认情况下,如果一个 cookie 没有设置这两个参数中的任何一个,那么在关闭浏览器之后,它就会消失。此类 cookie 被称为 “session cookie”。
二、为了让 cookie 在浏览器关闭后仍然存在,我们可以设置expires或max-age选项中的一个。

Expires

一、Expires 用于设置 Cookie 的过期时间。比如:

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;

二、当 Expires 属性缺省时,表示是会话性 Cookie,像上图 Expires 的值为 Session,表示的就是会话性 Cookie。
1、当为会话性 Cookie 的时候,值保存在客户端内存中,并在用户关闭浏览器时失效。需要注意的是,有些浏览器提供了会话恢复功能,这种情况下即使关闭了浏览器,会话期 Cookie 也会被保留下来,就好像浏览器从来没有关闭一样。
2、与会话性 Cookie 相对的是持久性 Cookie,持久性 Cookies 会保存在用户的硬盘中,直至过期或者清除 Cookie。这里值得注意的是,设定的日期和时间只与客户端相关,而不是服务端。
三、cookie 的到期日期,那时浏览器会自动删除它。
四、日期必须完全采用 GMT 时区的这种格式。我们可以使用date.toUTCString来获取它。

| 【示例】将 cookie 设置为 1 天后过期。```javascript // 当前时间 +1 天 let date = new Date(Date.now() + 86400e3); date = date.toUTCString(); document.cookie = “user=John; expires=” + date;

 |
| --- |

五、如果我们将expires设置为过去的时间,则 cookie 会被删除。
<a name="aNKDI"></a>
### Max-Age
一、Max-Age 用于设置在 Cookie 失效之前需要经过的秒数。比如:
```javascript
Set-Cookie: id=a3fWa; Max-Age=604800;

二、Max-Age 可以为正数、负数、甚至是 0。
1、如果 max-Age 属性为正数时,浏览器会将其持久化,即写到对应的 Cookie 文件中。
2、当 max-Age 属性为负数,则表示该 Cookie 只是一个会话性 Cookie。
3、当 max-Age 为 0 时,则会立即删除这个 Cookie。

// cookie 会在一小时后失效
document.cookie = "user=John; max-age=3600";

// 删除 cookie(让它立即过期)
document.cookie = "user=John; max-age=0";

三、假如 Expires 和 Max-Age 都存在,Max-Age 优先级更高。

Secure

一、

secure

二、标记为 Secure 的 Cookie 只能被通过 HTTPS 传输。
1、使用 HTTPS 安全协议,可以保护 Cookie 在浏览器和 Web 服务器间的传输过程中不被窃取和篡改。
三、默认情况下,如果我们在http://site.com上设置了 cookie,那么该 cookie 也会出现在https://site.com上,反之亦然。
1、也就是说,cookie 是基于域的,它们不区分协议。
四、使用此选项,如果一个 cookie 是通过https://site.com设置的,那么它不会在相同域的 HTTP 环境下出现,例如http://site.com。
五、如果一个 cookie 包含绝不应该通过未加密的 HTTP 协议发送的敏感内容,那么就应该设置这个选项。

// 假设我们现在在 HTTPS 环境下
// 设置 cookie secure(只在 HTTPS 环境下可访问)
document.cookie = "user=John; secure";

HTTPOnly

一、设置 HTTPOnly 属性可以防止客户端脚本通过 document.cookie 等方式访问 Cookie,有助于避免 XSS 攻击。
二、Web 服务器使用Set-Cookieheader 来设置 cookie。并且,它可以设置httpOnly选项。
三、这个选项禁止任何 JavaScript 访问 cookie。我们使用document.cookie看不到此类 cookie,也无法对此类 cookie 进行操作。
四、这是一种预防措施,当黑客将自己的 JavaScript 代码注入网页,并等待用户访问该页面时发起攻击,而这个选项可以防止此时的这种攻击。这应该是不可能发生的,黑客应该无法将他们的代码注入我们的网站,但是网站有可能存在 bug,使得黑客能够实现这样的操作。
五、通常来说,如果发生了这种情况,并且用户访问了带有黑客 JavaScript 代码的页面,黑客代码将执行并通过document.cookie获取到包含用户身份验证信息的 cookie。这就很糟糕了。
六、但是,如果 cookie 设置了httpOnly,那么document.cookie则看不到 cookie,所以它受到了保护。

http-only与secure的不同

一、http-only 无法被网页脚本读取,不限制传输通路的安全性。secure 可以被网页脚本读取,只允许通过安全通路发送给服务器
二、secure 管的是传输协议,设置了 secure 的 cookie 只能在 https 下传输。httponly 管的是可见性,设置了 httponly 的 cookie 对 js 脚本不可见

SameSite

作用

一、SameSite 属性可以让 Cookie 在跨站请求时不会被发送,从而可以阻止跨站请求伪造攻击(CSRF)。
1、该属性目前并不是所有浏览器都兼容。
(1)samesite会被到 2017 年左右的旧版本浏览器忽略(不兼容)。

属性值

一、SameSite 可以有下面三种值:

  • Strict:仅允许一方请求携带 Cookie,即浏览器将只发送相同站点请求的 Cookie,即当前网页 URL 与请求目标 URL 完全一致。
  • Lax:允许部分第三方请求携带 Cookie
  • None:无论是否跨站都会发送 Cookie

1、之前默认是 None 的,Chrome80 后默认是 Lax。

samesite=strict(和没有值的samesite一样)

一、如果用户来自同一网站之外,那么设置了samesite=strict的 cookie 永远不会被发送。
1、换句话说,无论用户是通过邮件链接还是从evil.com提交表单,或者进行了任何来自其他域下的操作,cookie 都不会被发送。
二、如果身份验证 cookie 具有samesite选项,那么 XSRF 攻击是没有机会成功的,因为来自evil.com的提交没有 cookie。因此,bank.com将无法识别用户,也就不会继续进行付款。
三、这种保护是相当可靠的。只有来自bank.com的操作才会发送samesitecookie,例如来自bank.com的另一页面的表单提交。
四、虽然,这样有一些不方便。
1、当用户通过合法的链接访问bank.com时,例如从他们自己的笔记,他们会感到惊讶,bank.com无法识别他们的身份。实际上,在这种情况下不会发送samesite=strict cookie。
2、我们可以通过使用两个 cookie 来解决这个问题:
(1)一个 cookie 用于“一般识别”,仅用于说 “Hello, John”,另一个带有samesite=strict的 cookie 用于进行数据更改的操作。
(2)这样,从网站外部来的用户会看到欢迎信息,但是支付操作必须是从银行网站启动的,这样第二个 cookie 才能被发送。

samesite=lax

一、一种更轻松的方法,该方法还可以防止 XSRF 攻击,并且不会破坏用户体验。
二、宽松(lax)模式,和strict模式类似,当从外部来到网站,则禁止浏览器发送 cookie,但是增加了一个例外。
1、如果以下两个条件均成立,则会发送samesite=laxcookie:
(1)HTTP 方法是“安全的”(例如 GET 方法,而不是 POST)。所有安全的 HTTP 方法详见RFC7231 规范。基本上,这些都是用于读取而不是写入数据的方法。它们不得执行任何更改数据的操作。跟随链接始终是 GET,是安全的方法。
(2)该操作执行顶级导航(更改浏览器地址栏中的 URL)。这通常是成立的,但是如果导航是在一个