用户身份认证-cookie

用户身份认证体系

简单说就是将 状态 存储在客户端的一种方式;
通常这个客户端是指浏览器;

cookie 本质是客户端存储的一块数据, 在请求时携带传输给服务器

一般分类

  1. 会话 cookie: 没有设置过期时间, 浏览器关闭后自动删除, 存储在浏览器内存中;
  2. 持久化 cookie: 有设置过期时间, 浏览器会存放到本地硬盘上, 下次打开浏览器还有效;

一般流程

  1. 浏览器发出一个请求到服务器

  2. 服务器将 cookie 放在响应的 http 包头内; 即 header 的 set-cookie 字段;

  3. 浏览器接收到这个 cookie
    如果没有过期时间, 则存储在内存中; 关闭浏览器后就丢弃;
    如果有设置过期时间, 则存储在磁盘上的一个 sqlite 文件内;

  4. 下次浏览器发起请求时, 会先查找到此 cookie 后, 并附加在请求头上发出;
    如果没有当前就不添加。

简单的身份认证场景

  1. 浏览器携带身份信息(账号 + 密码等)发送到服务器
  2. 服务器校验, 并生成一个 session 用以标识此会话,并生成一个 sessionID
  3. 服务器在响应 cookie 内存放这个 sessionID
  4. 浏览器后续请求都带上此 cookie,服务器便可以确定用户身份;
  5. 用户注销时, 服务器删除对应的 session;
    服务器也可以再最后一个响应中将 cookie 进行过期;

使用场景和内容

特别说明,cookie 对服务器和客户端都很有作用的;

因为数据存储在客户端磁盘上,关闭浏览器后下次打开目标网站时数据还可以用,
根据这个特性,可以有以下使用场景

  1. 记住用户名和密码; 用户点击后即登录;
  2. 用户自动登录, 服务器通过用户访问首页时就传来的 cookie 就能确定用户身份;
  3. 用户在未登录状态下的操作,下次还能恢复,如添加购物车,网页游戏进度;
  4. 可以存储用户离线期间的数据,下次网络恢复后再同步给服务器。
  5. 用户的身份ID或权限标记; 省去了服务器再查询一遍的步骤;

一般是也就是用户下次操作需要的东西,可以是客户端所需要的,也可以服务器所需要的。

因为 cookie 是可以伪造和篡改的, 所以一般存储加密的或不太重要的内容;

因为有大小限制,所以内容不适合太多;

另外还包含一些 cookie 自身的属性, 例如过期时间, 安全性等;

一个 cookie 只能包含一个自定义键 / 值对;
同个网站可以创建多个 cookie , 而多个 cookie 可以存放在同一个 cookie 文件中

客户端的物理存储

  1. 服务器的 response 响应头的 set-Cookie 字段进行设置;
  2. 保存在客户端本地的一个文件内, 实际是一个 sqlite3 db 内;
  3. 内容自动在请求的时候被传递给服务器
  4. 存储时先按 域名区分, 再按 path 区分

场景

每次请求都要携带的信息
通常内部包含一个唯一 ID, 或者再包括一些过期时间, 安全信息;
也可以包含一些特殊标记(如电商平台未登录账号时的购物车保持)

  1. 一台电脑多人使用不安全
  2. 使用多台电脑时, 不同步
  3. cookie 文件容易被删除
  4. 容量有大小限制, 不大于 4K
  5. 每次请求都发送, 占用带宽
  6. 不安全, 修改 hosts 后, 会将 cookie 发到非法站点
  7. 不同浏览器对每个域名的 cookie 数量有限制, 一般 20-50 条, 或者没有限制

特点

  1. 可以设置 cookie 生效的域, 可以是当前域以及当前域下的所有子域

  2. 可以设置过期的时间, 默认是会话结束的时候, 当时间到期自动销毁

  3. 因为每次请求都会带上 cookie, 所以不宜太大

  4. 明文存放和传输, 所以不适合放置敏感信息

  5. 因大小限制, 所以不适合放置大量数据

  6. 由浏览器实现和管理的

  7. 一个域名下存放的 cookie 的个数是有限制的, 不同的浏览器存放的个数不一样,一般为 20 个

大小限制: 4096b = 4k;
4k 并不是一个域名下所有的 cookie 共享的,而是一个 name 的大小, 即一个键的大小

格式: 文本形式的键值对;

生存期:
可以设置 expire 过期时间, 自动触发浏览器的删除机制;
缺省当浏览器关闭进程的时候自动销毁

使用范围: 相同域

需使用 P3P 协议(Platform for Privacy Preferences),
通过 P3P 使用户自己可以指定浏览器的隐私策略, 达到存储第三方 Cookie 的目的,
只需要在响应用户请求时, 在 HTTP 的头信息中增加关于 P3P 的配置信息就可以了

域就是 domain 的概念, 因为浏览器是个注意安全的环境,
所以不同的域之间是不能互相访问 cookie 的(当然可以通过特殊设置的达到 cookie 跨域访问);

路径就是 routing 的概念, 一个网页所创建的 cookie 只能被与这个网页在同一目录或子目录下得所有网页访问,
而不能被其他目录下得网页访问

  1. 浏览的当前网站本身设置的 cookie
  2. 来自在网页上嵌入广告或图片等其他域来源的 第三方 cookie (网站可通过使用这些 cookie 跟踪你的使用信息)
  1. 通过浏览器工具清除 cookie
  2. 设置有效期

浏览器可以通过设置来接受和拒绝访问 cookie

是个字符串, 但这个字符串中等号, 分号, 空格被当做了特殊符号
所以当 cookie 的 key 和 value 中含有这 3 个特殊字符时, 需要对其进行额外编码,
一般会用 escape 进行编码, 读取时用 unescape 进行解码

通常 cookie 的域和浏览器地址的域匹配, 这被称为第一方 cookie;
那么第三方 cookie 就是 cookie 的域和地址栏中的域不匹配, 这种 cookie 通常被用在第三方广告网站;
为了跟踪用户的浏览记录, 并且根据收集的用户的浏览习惯, 给用户推送相关的广告

存储认证信息的场景

  1. 服务器不存 session, 完全依赖 cookie 存放的自定义加密认证信息, 服务端的无状态化
  2. 服务器存 session, 客户端 cookie 内存放 sessionID

优点

  1. 通常适用于单个域名, 除非显式配置, 否则, 浏览器不会随请求一起发送 Cookie 到其他域名

缺点

  1. 在不需要身份验证的 url 中也会带上 cookie, 容易被 CSRF 攻击利用;
  2. 只适合浏览器, 不适合其他应用

安全措施

1
2
3
Set-Cookie: <cookie-name>=<cookie-value>; Secure
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly
Set-Cookie: <cookie-name>=<cookie-value>; SameSite=Lax

业务场景安全措施

cookie 如果存放认证信息, 需要进行加密;
每次请求服务器都需要对此 key 进行解密, 以防止伪造

跨域

默认情况下, 浏览器不会发送当前域的 cookie 到另一个域, 除非请求时显示指定
withCredentials = true

跨域(不同子域的 SSO)

设置 cookie 时, 将 domain 设置为大家都能访问的父域

1
domain=.abc.test;path=/

则 xxx.abc.test 将都能使用这个 cookie

Comment

cookie 的产生着对该 cookie 的描述

Domain

定义可访问该 cookie 的域名
对一些大的网站, 如果希望 cookie 可以在子网站中共享, 可以使用该属性,
例如设置 Domain 为 .bigsite.com,
则 sub1.bigsite.com 和 sub2.bigsite.com 都可以访问已保存在客户端的 cookie, 这时还需要将 Path 设置为 /

客户端设置

1
document.cookie ="username=cfangxu;path=/;domain=bigsite.com"

服务器端设置

1
Set-Cookie: username=cfangxu;path=/;domain=bigsite.com

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 个值可用:

  1. SameSite=Lex 将确保浏览器不会在跨站点请求时发送 Cookie(默认行为)
  2. SameSite=Strict 将确保浏览器仅针对同站点请求发送 Cookie
  3. 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 天

1
2
3
4
5
6
7
8
示例
function setCookie(c_name, value, expiredays){
    var exdate=new Date();
    exdate.setDate(exdate.getDate() + expiredays);
    document.cookie=c_name+ "=" + escape(value) + ((expiredays==null) ? "" : ";expires="+exdate.toGMTString())
}

setCookie('username','cfangxu',30)

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, 正数

  1. 负数: 有效期 session;
  2. 0: 删除 cookie;
  3. 正数: 有效期为创建时刻 + max-age

参考设置项

1
2
3
4
5
6
7
Set-Cookie: <cookie-name>=<cookie-value>
Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>    # 指定具体的过期时间
Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<number>  # 多少 s 后过期, 优先级高于 expires
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain>
Set-Cookie: <cookie-name>=<cookie-value>; Path=<path>
Set-Cookie: <cookie-name>=<cookie-value>; Secure            # 只能通过 https 传输
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly

服务器端设置

response header 中有一项叫 set-cookie, 是服务端专门用来设置 cookie 的

1
Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure]

一个 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 的下列选项:

  1. expires
  2. domain
  3. path
  4. secure(有条件: 只有在 https 协议的网页中, 客户端设置 secure 类型的 cookie 才能成功)

客户端无法设置 HttpOnly 选项

客户端读取

通过 document.cookie 来获取当前网站下的 cookie; 得到的字符串形式的值, 为当前网站下所有的 cookie;
为避免跨域脚本(xss)攻击, 这个方法只能获取非 HttpOnly 类型的 cookie
会把所有的 cookie 通过一个分号 + 空格的形式串联起来,
例如 username=chenfangxu;job=coding

  1. 重新赋值
  2. 在设置新 cookie 时, path/domain 这几个选项一定要旧 cookie 保持一样;
    否则不会修改旧值, 而是添加了一个新的 cookie

删除

将过期时间设置成已过去的时间,
path/domain/ 这几个选项一定要旧 cookie 保持一样

注意:
如果只设置一个值, 那么算 cookie 中的 value;
设置的两个 cookie, key 值如果设置的相同, 下面的也会把上面的覆盖

设置 max-age=0

前端设置过期时间

1
2
3
4
5
6
7
8
9
document.cookie = '名称 = 值;expires=' + GMT(格林威治时间)格式的日期型字符串;

function setCookie(c_name, value, expiredays) {
    var exdate=new Date();
    exdate.setDate(exdate.getDate()+expiredays)
    document.cookie=c_name+"="+escape(value)+((expiredays==null) ? "" : ";expires="+exdate.toGMTString());
}

setCookie('username','cfangxu',30)

微信搜索IT运维小秋

Licensed under CC BY-NC-SA 4.0
转载或引用本文时请遵守许可协议,知会作者并注明出处
不得用于商业用途!
最后更新于 2023-02-10 00:00 UTC