从cookie说开去

311 阅读6分钟

前端的储存技术

  • 打开Chrome浏览器,F12打开控制台,点击Application项,侧边栏Storage栏目下的6种选项,都是web端的储存方式
    • LocalStorage
    • SessionStorage
    • Cookies
    • IndexedDB
    • Web SQL
    • Trust Tokens

      常用前三种存储方式,这里重点讲cookies image.png

Cookies

  • 定义:

    • HTTP Cookie,通常直接叫做cookie,用于客户端存储会话信息的,该标准要求服务器对任意HTTP请求发送Set-Cookie HTTP头作为响应的一部分,其中包含会话信息。
    • Cookie保存在客户端浏览器中,作为Http请求的一部分,浏览器请求页面时,它会被通过Http请求头的形式自动发送过去。 image.png image.png

    第一次请求时,服务器端将cookie存放在响应头的Set-Cookie字段中,发送到客户端,客户端将cookies数据存在在浏览器内存里面。
    在下一次请求时,浏览器将会自动把cookies数据存放在请求头的Cookie字段中,带回服务器端。

  • 配置选项功能详解:

    1. Name & Value

      • Name表示Cookie的名称,服务器就是通过name属性来获取某个Cookie的值。
      • Value表示Cookie的值,多数情况下服务器会把这个value当做一个Key去缓存中查询保存的数据。
    2. SameSite:Strict, Lax, None

      SameSite属性用来限制第三方 Cookie,使cookie不能随着跨域请求一起发送,从而防范跨站请求伪造攻击(CSRF),减少安全风险。

      • Cookie 的 SameSite 属性

      • Strict: 最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。

        跨域和跨站:
        跨域: 当站点和站点之间有着相同的scheme相同的hostname相同的port时,被认为是同域。这三个中的任何一个不相同时被认为是跨域
        跨站: 当站点和站点的顶级域名与它前面的域(不需要考虑协议和端口)都一致时,被认为是同站。反之被认为是跨站
        判断手段: 通过检查HTTP请求头选项Sec-Fetch-Site的值,可以确定请求与请求之间是“同站same-site”,“同源same-origin”还是“跨站cross-site”

      • Lax: 规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求(链接,预加载请求,GET 表单)除外。

      • None: None无论是否跨站都会发送 Cookie, 但必须同时设置Secure属性(Cookie 只能通过 HTTPS 协议发送)才有作用,否则将会遭到客户端的Block拒绝,无法写入cookie。 image.png

      Cookie 往往用来存储用户的身份信息,恶意网站可以设法伪造带有正确 Cookie 的 HTTP 请求,这就是 CSRF 攻击,设置了StrictLax以后,基本就杜绝了 CSRF 攻击。

      Chrome93 已将Lax变为SameSite默认设置,无法通过chrome://flags/网站选择显式关闭SameSite属性将其设为None

    3. HttpOnly: true/false;

      • 设置了 HttpOnly 属性为true的 cookie,将不能使用 JavaScript 经由 Document.cookie 属性、XMLHttpRequestRequest APIs 进行访问,以防范跨站脚本攻击。

        当HttpOnly为true时,客户端对cookie既无法获取修改,也无法删除。
        客户端也无法去添加带有HttpOnly属性的cookie, 但若想在客户端调试HttpOnly属性,可以在Chrome打开Application,手动去把cookie相应的HttpOnly属性进行打勾 image.png

    4. Secure: true/false;

      • 一个带有 secure 安全属性的 cookie, 只有在请求使用SSL和HTTPS协议的时候才会被发送到服务器。
    5. Expires/Max-Age

      • Expires 为 Cookie 的删除设置一个过期的GMT日期
      • Max-Age 设置一个 Cookie 将要过期的
      • 注意:
        1. 在 Cookie 中同时设置了 Expires 和 Max-Age, 将会忽略 expires,取Max-Age

        2. 如果 Expires 和 Max-Age两个都没有显式设置,Cookie将会作为一个Session Cookie,当关闭浏览器时它会被删除

          Session Cookie数据保存在内存中,如果是设置了过期时间,浏览器会把cookie保存在硬盘内

        3. 浏览器根据本地时间,决定 Cookie 是否过期,到了指定时间以后,浏览器就不再保留这个 Cookie

          由于本地时间未必精准,所以无法保证Cookie一定会在服务器指定的时间过期。

    6. Domain

      • Cookie的作用域:Cookie的作用域是Domain本身,以及Domain下的所有子域名。

        Cookie只有在当前域和子域中查看,在父域或者兄弟域之中是无法跨域读取cookie的

      • 参数值:
        • 当没有显式设置Domain值时,Domain值默认为当前域名
          • Cookie是前端设置的,当前域名便是前端网站的访问域名
          • Cookie是后端设置的,当前域名便是后端的接口域名
        • 参数值可以为设置父域名以及自身,但不能设置其它域名,包括子域名,否则cookie不起作用。

          如果要在多个二级域名中共享Cookie的话,那么得将Domain的属性设置为顶级域名。
          在设置为顶级域名时,记得以.开头,如.baidu.com

    7. Path: 在Domain的作用域下,进一步通过域名路径限制访问。

      • 参数值:默认为根路径/,即Domain域名作用域下的所有页面路径,皆可访问到cookie
    8. SameParty:目前只在Chrome看到该规范

    9. Priority:优先字段,值为 low|medium|high,medium 为默认值。目前只在Chrome看到该规范

      • 目的:旨在减少 cookie“驱逐”(即在超过每个域的 cookie 容量限制时删除)对用户体验的影响。
      • 基本场景:
        • 场景问题:长期存在的登录会话 cookie 由于其年龄而被驱逐,从而迫使用户丢失会话。当用户重新进行身份验证时,会发生 Cookie“恢复”。
        • 建议解决方案:为 cookie 添加一个“优先级”字段,从而允许服务器保护重要的 cookie。
      • 作用:
        • 在重新验证 cookie 时对其进行优先级排序。
        • 当 cookie 超过每个域的 cookie 容量时,它们会被删除。
        • cookie-priority 允许服务器以较低的优先级删除旧的 cookie,并在更高优先级的 cookie 上停留更长时间。
  • Cookie实践:

    • 运行环境: Google Chrome 版本 94.0.4606.54(正式版本) (64 位)
    • 前端设置方法:
      • HTTP cookies
      • 原生示例:
        let date = new Date();
        date.setDate(date.getDate()+30);
        date.toGMTString();
        
        document.cookie="tokens=GFRTDDFG;SameSite=Strict;Secure;Expires="+ date +";"
        document.cookie="tokens=GFRTDDFG;SameSite=Strict;Secure;max-age=1000;"
        
      • vue示例(vue-cookies)
        Vue.$cookies.set("use_path_argument","value",null, null, null, null, "Lax");
        // Vue.$cookies.set(keyName, value[, expireTimes[, path[, domain[, secure[, sameSite]]]]])
        
    • 后端设置方法:Node示例
      // 方法1.
      res.setHeader("Set-Cookie", "id=123456; Path=/;SameSite=None; Secure")
      
      // 方法2. 
      const oneDayToSeconds = 24 * 60 * 60;
      const token = 123456;
      res.cookie('token', token,  
      { maxAge: oneDayToSeconds, // Cookie 将要过期的秒数,若没有设置,这个 Cookie 将会一直存在直到你关闭浏览器,这种称之为 `Session Cookie`
        httpOnly: false, // You can't access these tokens in the client's javascript
        sameSite: '', // set sameSite - should be one of `None`, `Strict` or `Lax`. 
        // signed: true, // use the secret passed into `express.cookieParser('secret')` to sign the value.
        secure: process.env.NODE_ENV === 'production'? true: false // Forces to use https in production
      })