首页 > 技术文章 > 理解Cookie、Session和token机制,及其安全问题

yunmuq 2022-01-13 18:50 原文



一、Cookie

Cookie严格来说是存储空间 + 通信机制,是个载体,用提交持久化的信息。

浏览器将一条条数据存储在一个叫Cookies的空间中,这些数据的结构包含:键、值、属性

Cookie的通信机制为:浏览器中某个网站(域)发送HTTP请求时,浏览器会自动将此域的所有Cookie放在所有HTTP报文的Cookie Header中,抓包能发现



二、Session

Session是身份认证的凭证,用户登录后,服务器分发一个凭证,以后用户再请求带上这个凭证就能明确身份,不用每次都用账号密码来证明身份

这么重要的数据当然得保存在服务端,且以服务端的为准。但客户端也得保存,才能在登录周期中每次请求都带上

"每次请求都带上"这显然就和Cookie一样,所以就有很多开发者把Session放在Cookies里,不用自己写前端代码把Session塞进请求里,浏览器自己会带上

而浏览器开发者也觉得这样很合理,也为Cookie添加了一些可设置的安全相关属性



三、Cookie和Session的区别

百度一下最常见的就是"Cookie保存在客户端而Session保存在服务端",很多人看了有疑惑,明明Session就在Cookie中啊,为什么这么说?二者到底有啥区别?

但当我们说Cookie和Session的区别时,这里的Cookie就理应是一种和Session一样的认证机制,这种情况下Session借助Cookies暂存在浏览器中,二者都在浏览器和服务器中被存储,这是Cookies最重要的应用

那如何解释"Cookie保存在客户端"呢?可能本来就是瞎说,也可能是最开始有这种场景:

网站设计时,服务端可能每次收到请求都需要知道客户端的某种信息,比如用户登录时间,这种信息又不足以重要到需要保存在服务端,那么可以借助Cookie

数据在前端生成,或在服务端生成,生成后通过Set-Cookie发送给客户端后,服务端自身不保存,当客户端再次有请求时,服务端可以从请求的HTTP Header中调出这个数据

以及传闻中cookie名字的由来:

设想你某次登陆过一个网站,下次登录的时候不想再次输入账号了,怎么办?这个信息可以写到Cookie里面,访问网站的时候,网站页面的脚本可以读取这个信息,就自动帮你把用户名给填了,能够方便一下用户。这也是Cookie名称的由来,给用户的一点甜头。

我比较认可的解释是Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式[1]



四、token

其实就字面意思来说,token(令牌),应该是用户凭证,服务器校验它来判断当前请求是否来自于登录用户。

而Session(会话),应该是一次连续的、短暂的交流。在交流过程中,服务器可以通过Session识别这次会话,区别于其它正在进行的会话。这里的会话可以是一次支付业务,比如用户创建了订单,带着一个session跳到支付平台,回来服务器再校验是否支付成功。这种会话有明确的起点、终点,且不能被中断,比如你关闭浏览器,再打开页面,你仍是登录状态,但是支付流程却只能重新走。这种特性让session在浏览器上更适合存放在Session Storage中

然而现实和字面意思却相反,开发者们习惯用Session来做令牌,用token来做会话,其它用于校验身份的参数也习惯命名为token



五、token和Session的区别

Session机制通过Cookie来实现。只需在登录成功后,响应中有Set-Cookie Header来设置一个Cookie值,浏览器就会自动保存、刷新Cookie值,后续请求会自动携带此Cookie值,完全不用前端代码

而token机制一般不通过Cookie来实现,它一般通过自定义一个HTTP Header来实现,或放在HTTP参数中来实现,需要前端手动将其塞进报文中

即使token为了长期存储放在Cookie中,后端最好也不要通过Cookie Header来校验,比如csrf-token,就得通过HTTP Header、HTTP参数来校验才有意义



六、三者涉及的用户认证安全问题

目前的浏览器有以下空间供网站使用

  • Cookies:持久保存,大小限制4kb,最多放20个,每次请求都会带上所有Cookie。包含特殊功能属性

  • Session Storage:关闭标签后失效,请求不会主动带上

  • Local Storage:持久保存,请求不会主动带上

我们再来看几个Web的安全漏洞

  • XSS,跨站脚本攻击。攻击者获得页面的js权限,能操作页面的一切,包括Session Storage和未设置HTTP Only属性的Cookie

  • CSRF,跨站请求伪造,就是钓鱼网站,在网站A,发送一个向网站B的请求,由于浏览器会在请求中加上此域的Cookie,所以如果在浏览器登录了网站B,又打开了钓鱼网站A,网站A构造的前往网站B的请求就会带上你的Cookie,如果Cookie中有用户凭证,服务器认为你是登录用户,请求就会被响应。比如点赞、关注、发色情广告等请求

防御XSS,把关机Cookie设置HTTP Only属性即可,js无权访问

而防御CSRF,推荐使用token机制,服务器颁发token,按规范来说推荐存储在Session Storage中(也可以放cookie中,由于浏览器的同源策略,网站A无法访问网站B的cookie),js将其添加到请求的参数或Header中

以上两个措施,都需要做到,才能防护这两个攻击。

另外博主以为,现在服务器校验referer Header也是能防御CSRF的



四、错误示范

  • 把用户登录凭证放在Cookie中,未设置HTTP Only,无token机制

    容易遭受XSS、CSRF攻击

  • 把用户登录凭证放在Cookie中,设置了HTTP Only,无token机制

    容易遭受CSRF攻击,成功防御XSS攻击

  • 把用户登录凭证放在Cookie中,未设置HTTP Only,无token机制(前端把token保存在Session Storage。或保存在Cookies且未设置HTTP Only,后端通过HTTP Header、HTTP参数来校验)

    容易遭受XSS攻击,攻击者能构造请求,把token拼上,成功防御CSRF攻击

  • 把用户登录凭证放在Cookie中,设置了HTTP Only,请求中有token参数/Header【规范防御】

    成功防御XSS、CSRF攻击

  • 把用户登录凭证放在Cookie中,未设置HTTP Only,请求中有token参数/Header(前端把token保存在保存在Cookies,设置了HTTP Only)【稀里糊涂地防御】

    成功防御XSS、CSRF攻击。但不可能,Cookie都未设置HTTP Only,token为啥会这么巧设置了

  • 把用户登录凭证放在Cookie中,设置了HTTP Only,且请求参数/Header中也添加并校验此凭证【想屁吃】

    当你以为这样后端用一串随机字符串就能解决校验的时候,觉得发现一个省事窍门的时候,浏览器会告诉你,设置了HTTP Only的Cookie,js无法拿到,无法放到请求参数/Header中

  • 把用户登录凭证放在Cookie中,设置了HTTP Only,且服务器校验referer Header【极简防御二】

    一个凭证同时防御XSS、CSRF攻击,浏览器js目前无法修改请求的referer头,会提示Refused to set unsafe header "Referer"



五、最后

现在浏览器的安全也逐步做上来了,一定程度上能防御CSRF,除了cookie samesite,跨域在POST请求前,还会发送OPTIONS请求,如果OPTIONS请求不通过,无法POST

但开发还是要规范,不能指望用户都是新浏览器

以上,讲得不对望指正



推荐阅读