什么是 cookie
简单说就是将 状态 存储在客户端的一种方式;
通常这个客户端是指浏览器;
cookie 本质是客户端存储的一块数据, 在请求时携带传输给服务器
一般分类
- 会话 cookie: 没有设置过期时间, 浏览器关闭后自动删除, 存储在浏览器内存中;
- 持久化 cookie: 有设置过期时间, 浏览器会存放到本地硬盘上, 下次打开浏览器还有效;
一般流程
-
浏览器发出一个请求到服务器
-
服务器将 cookie 放在响应的 http 包头内; 即 header 的 set-cookie 字段;
-
浏览器接收到这个 cookie
如果没有过期时间, 则存储在内存中; 关闭浏览器后就丢弃;
如果有设置过期时间, 则存储在磁盘上的一个 sqlite 文件内; -
下次浏览器发起请求时, 会先查找到此 cookie 后, 并附加在请求头上发出;
如果没有当前就不添加。
简单的身份认证场景
- 浏览器携带身份信息(账号 + 密码等)发送到服务器
- 服务器校验, 并生成一个 session 用以标识此会话,并生成一个 sessionID
- 服务器在响应 cookie 内存放这个 sessionID
- 浏览器后续请求都带上此 cookie,服务器便可以确定用户身份;
- 用户注销时, 服务器删除对应的 session;
服务器也可以再最后一个响应中将 cookie 进行过期;
使用场景和内容
特别说明,cookie 对服务器和客户端都很有作用的;
因为数据存储在客户端磁盘上,关闭浏览器后下次打开目标网站时数据还可以用,
根据这个特性,可以有以下使用场景
- 记住用户名和密码; 用户点击后即登录;
- 用户自动登录, 服务器通过用户访问首页时就传来的 cookie 就能确定用户身份;
- 用户在未登录状态下的操作,下次还能恢复,如添加购物车,网页游戏进度;
- 可以存储用户离线期间的数据,下次网络恢复后再同步给服务器。
- 用户的身份ID或权限标记; 省去了服务器再查询一遍的步骤;
一般是也就是用户下次操作需要的东西,可以是客户端所需要的,也可以服务器所需要的。
因为 cookie 是可以伪造和篡改的, 所以一般存储加密的或不太重要的内容;
因为有大小限制,所以内容不适合太多;
另外还包含一些 cookie 自身的属性, 例如过期时间, 安全性等;
一个 cookie 只能包含一个自定义键 / 值对;
同个网站可以创建多个 cookie , 而多个 cookie 可以存放在同一个 cookie 文件中
客户端的物理存储
- 服务器的 response 响应头的 set-Cookie 字段进行设置;
- 保存在客户端本地的一个文件内, 实际是一个 sqlite3 db 内;
- 内容自动在请求的时候被传递给服务器
- 存储时先按 域名区分, 再按 path 区分
场景
每次请求都要携带的信息
通常内部包含一个唯一 ID, 或者再包括一些过期时间, 安全信息;
也可以包含一些特殊标记(如电商平台未登录账号时的购物车保持)
cookie 缺点
- 一台电脑多人使用不安全
- 使用多台电脑时, 不同步
- cookie 文件容易被删除
- 容量有大小限制, 不大于 4K
- 每次请求都发送, 占用带宽
- 不安全, 修改 hosts 后, 会将 cookie 发到非法站点
- 不同浏览器对每个域名的 cookie 数量有限制, 一般 20-50 条, 或者没有限制
特点
-
可以设置 cookie 生效的域, 可以是当前域以及当前域下的所有子域
-
可以设置过期的时间, 默认是会话结束的时候, 当时间到期自动销毁
-
因为每次请求都会带上 cookie, 所以不宜太大
-
明文存放和传输, 所以不适合放置敏感信息
-
因大小限制, 所以不适合放置大量数据
-
由浏览器实现和管理的
-
一个域名下存放的 cookie 的个数是有限制的, 不同的浏览器存放的个数不一样,一般为 20 个
cookie 细节
大小限制: 4096b = 4k;
4k 并不是一个域名下所有的 cookie 共享的,而是一个 name 的大小, 即一个键的大小
格式: 文本形式的键值对;
生存期:
可以设置 expire 过期时间, 自动触发浏览器的删除机制;
缺省当浏览器关闭进程的时候自动销毁
使用范围: 相同域
Cookie 跨域, 主要是为了统一应用平台, 实现单点登录
需使用 P3P 协议(Platform for Privacy Preferences),
通过 P3P 使用户自己可以指定浏览器的隐私策略, 达到存储第三方 Cookie 的目的,
只需要在响应用户请求时, 在 HTTP 的头信息中增加关于 P3P 的配置信息就可以了
cookie 域和路径
域就是 domain 的概念, 因为浏览器是个注意安全的环境,
所以不同的域之间是不能互相访问 cookie 的(当然可以通过特殊设置的达到 cookie 跨域访问);
路径就是 routing 的概念, 一个网页所创建的 cookie 只能被与这个网页在同一目录或子目录下得所有网页访问,
而不能被其他目录下得网页访问
cookie 存在两种类型
- 浏览的当前网站本身设置的 cookie
- 来自在网页上嵌入广告或图片等其他域来源的 第三方 cookie (网站可通过使用这些 cookie 跟踪你的使用信息)
cookie 有两种清除方式
- 通过浏览器工具清除 cookie
- 设置有效期
浏览器可以通过设置来接受和拒绝访问 cookie
cookie 的编码
是个字符串, 但这个字符串中等号, 分号, 空格被当做了特殊符号
所以当 cookie 的 key 和 value 中含有这 3 个特殊字符时, 需要对其进行额外编码,
一般会用 escape 进行编码, 读取时用 unescape 进行解码
第三方 cookie
通常 cookie 的域和浏览器地址的域匹配, 这被称为第一方 cookie;
那么第三方 cookie 就是 cookie 的域和地址栏中的域不匹配, 这种 cookie 通常被用在第三方广告网站;
为了跟踪用户的浏览记录, 并且根据收集的用户的浏览习惯, 给用户推送相关的广告
存储认证信息的场景
- 服务器不存 session, 完全依赖 cookie 存放的自定义加密认证信息, 服务端的无状态化
- 服务器存 session, 客户端 cookie 内存放 sessionID
优点
- 通常适用于单个域名, 除非显式配置, 否则, 浏览器不会随请求一起发送 Cookie 到其他域名
缺点
- 在不需要身份验证的 url 中也会带上 cookie, 容易被 CSRF 攻击利用;
- 只适合浏览器, 不适合其他应用
安全措施
|
|
业务场景安全措施
cookie 如果存放认证信息, 需要进行加密;
每次请求服务器都需要对此 key 进行解密, 以防止伪造
跨域
默认情况下, 浏览器不会发送当前域的 cookie 到另一个域, 除非请求时显示指定
withCredentials = true
跨域(不同子域的 SSO)
设置 cookie 时, 将 domain 设置为大家都能访问的父域
|
|
则 xxx.abc.test 将都能使用这个 cookie
Cookie 的本身属性(可选项)
Comment
cookie 的产生着对该 cookie 的描述
Domain
定义可访问该 cookie 的域名
对一些大的网站, 如果希望 cookie 可以在子网站中共享, 可以使用该属性,
例如设置 Domain 为 .bigsite.com,
则 sub1.bigsite.com 和 sub2.bigsite.com 都可以访问已保存在客户端的 cookie, 这时还需要将 Path 设置为 /
客户端设置
|
|
服务器端设置
|
|
Path
定义网站上可以访问 cookie 的页面的路径
缺省状态下 Path 为产生 cookie 时的路径, 此时 cookie 可以被该路径以及其子路径下的页面访问;
可以将 Path 设置为 /, 使 cookie 可以被网站下所有页面访问
也可以限制如只有 /doc 能使用这个 cookie
path默认值为设置该cookie的网页所在的路径
document.cookie = “username=cfangxu; path=/”
Set-Cookie:name=cfangxu; path=/blog
注意: /blog 会匹配 /blogxxxxx 等以 /blog 开头的路径
Secure
定义 cookie 的安全性, 缺省为 false
Secure 为 false 时则可在 HTTP 状态下传递 cookie
当该值为 true 时必须是 HTTPS 状态下 cookie 才从客户端附加在 HTTP 消息中发送到服务端,
在 HTTP 时 cookie 是不发送的;
客户端设置时,在http协议的网页中是无法设置secure类型cookie的,
一般在服务器端设置
Version
定义 cookie 的版本, 由 cookie 的创建者定义
httpOnly
用来设置cookie是否能通过 js 去访问
默认情况下,cookie不会带httpOnly选项,所以 js 可读写
当cookie带httpOnly选项时,客户端则无法通过js代码去访问,包括读写
客户端也不可以主动设置一个带 httpOnly 选项的 cookie
保护 Cookie 免受 XSS 跨站攻击
设置了 HttpOnly 属性的 cookie 后
不能使用 JavaScript 经由 Document.cookie 属性, XMLHttpRequest 和 Request APIs 进行访问
SameSite
规定浏览器不能在跨域请求中携带 cookie, 减少 CSRF 攻击
SameSite 属性有 3 个值可用:
- SameSite=Lex 将确保浏览器不会在跨站点请求时发送 Cookie(默认行为)
- SameSite=Strict 将确保浏览器仅针对同站点请求发送 Cookie
- SameSite=Note 将允许通过跨站点和同站点请求发送 Cookie
==========================================================
expires
创建 cookie 时如果不指定生存有效时间, 默认;
则 cookie 只在浏览器关闭前有效;关闭浏览器进程的时候自动销毁
cookie 会在服务器端和客户端传输, 但是不会保存在客户机的磁盘上
打开新的浏览器将不能获得原先创建的 cookie 信息
如果 cookie 定义了有效期, 则会保存在客户端本地磁盘
document.cookie = ’ 名称 = 值;expires=’ + GMT(格林威治时间)格式的日期型字符串;
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2018 07:28:00 GMT;
一般设置天数: newDate().setDate(oDate.getDate()+5); 比当前时间多 5 天
|
|
Max-Age
cookie 的有效时间, 用秒计数,
当超过有效期后, cookie 的信息不会从客户端附加在 HTTP 消息头中发送到服务端
expires 是 http/1.0 协议中的选项,
在新的 http/1.1 协议中 expires 已经由 max-age 选项代替, 两者的作用都是限制 cookie 的有效时间;
expires 的值是一个时间点(cookie 失效时刻 = expires),
而 max-age 的值是一个以秒为单位时间段(cookie 失效时刻 = 创建时刻 + max-age)
max-age 的默认值是 -1(即有效期为 session );
max-age 有三种可能值: 负数, 0, 正数
- 负数: 有效期 session;
- 0: 删除 cookie;
- 正数: 有效期为创建时刻 + max-age
参考设置项
|
|
cookie 的相关的操作
服务器端设置
response header 中有一项叫 set-cookie, 是服务端专门用来设置 cookie 的
|
|
一个 set-Cookie 字段只能设置一个 cookie, 如要设置多个 cookie, 需要添加同样多的 set-Cookie 字段
服务端可以设置 cookie 的所有选项: expires, domain, path, secure, HttpOnly
通过 Set-Cookie 指定的这些可选项只会在浏览器端使用, 而不会被发送至服务器端
客户端设置
document.cookie = ’ 名字 = 值 ‘;
document.cookie = ‘username=cfangxu;domain=baike.baidu.com’
// 设置生效域
客户端可以设置 cookie 的下列选项:
- expires
- domain
- path
- secure(有条件: 只有在 https 协议的网页中, 客户端设置 secure 类型的 cookie 才能成功)
客户端无法设置 HttpOnly 选项
客户端读取
通过 document.cookie 来获取当前网站下的 cookie; 得到的字符串形式的值, 为当前网站下所有的 cookie;
为避免跨域脚本(xss)攻击, 这个方法只能获取非 HttpOnly 类型的 cookie
会把所有的 cookie 通过一个分号 + 空格的形式串联起来,
例如 username=chenfangxu;job=coding
修改 cookie
- 重新赋值
- 在设置新 cookie 时, path/domain 这几个选项一定要旧 cookie 保持一样;
否则不会修改旧值, 而是添加了一个新的 cookie
删除
将过期时间设置成已过去的时间,
path/domain/ 这几个选项一定要旧 cookie 保持一样
注意:
如果只设置一个值, 那么算 cookie 中的 value;
设置的两个 cookie, key 值如果设置的相同, 下面的也会把上面的覆盖
删除 cookie
设置 max-age=0
前端设置过期时间
|
|