另一个角度看Cookie

cookie-another-perspective

通常,移动端工程师对Cookie的认知源于和服务器交互时,在HTTP请求中,添加的name为Cookie的header。“Cookie,用来保持用户登陆状态”。那么,Cookie和登录状态是否等价呢?这篇文章,我们将换一个角度,从CS交互、服务端对会话的处理等方面对Cookie进行讨论。

会话和Cookie

“登录状态”,是会话(Session)的一种特例。会话是指HTTP协议的客户端和服务端,在进行多次通信过程中得以保持的某种状态。要理解这一点,首先要知道HTTP请求是无状态的:客户端(浏览器)每次向服务器发送一次HTTP请求,对于服务器来说,都是一次全新的请求。服务器无法识别这个请求和上一个请求的关系。

当WEB服务需要提供跨域请求的功能时,比如:设置网站的显示语言,就需要Session的概念。服务器可以对HTTP请求进行标记,将其归属于一个会话,下次再带有该标记的请求,可以看作是同一个会话的不同请求。

Session是服务端的概念,Cookie是HTTP协议的一部分

Session是服务端的概念,为了在客户端和服务端的通信中保持Session,服务器需要对HTTP请求进行标记,而这个标记,通常就是通过Cookie实现的。

Cookie的定义来源于HTTP协议,最初的目的,也就是为了在通信中保持Session。也就是说,Cookie是HTTP协议中的一部分,支持HTTP协议的客户端、服务端,都应该遵照HTTP协议,对Cookie进行管理和使用。

服务器对Session的处理
当服务器收到一个请求时,就会在HTTP请求头中查找Cookie,如果发现用于Session标记的Cookie,就说明该请求包含在一次会话当中,对该请求的处理,应基于会话,比如:展示登录后的信息、使用该会话的配置项等等;相反,如果没有找到Cookie,说明该请求是客户端第一次和服务器通信,这时服务器就会初始化一个Session,同时为该Session生成一个唯一标示,叫做Session id。
这个Session id将在服务器给客户端返回响应时,作为Cookie,通过HTTP响应头中的Set-Cookie返回给客户端。下一次客户端发起请求时,应在请求头中包含该Cookie。

“登录状态”也是一种会话,表示某个用户,通过身份验证(密码、证书、网络信息等等),使用该用户的身份开启了和服务器的一次会话。所以,通常情况下,已经是登录状态的客户端,会保存对应这次会话的Session id,也就是Cookie。

同时,因为Session由服务器管理,所以客户端在本地无法验证Cookie所包含的Session id是否有效;除非向服务器发送请求。

Cookie和Token

相较于Cookie,还有一个非常常见的名词:Token,经常和登录状态一起被提到。那么Cookie和Token有什么关系呢?

Token翻译成中文是“令牌”的意思,“令牌”是用户身份的证明,就如同身份证对于公民、车牌对于车。持有“令牌”,表示拥有该用户的身份。前文提到,对于服务器来说,“登录状态”是由服务器的Session进行管理,所以Token,也就是服务器给登录的用户(客户端)发放的令牌,客户端持有该令牌访问服务器,服务器就认为客户端拥有该用户的身份。

Token是身份的证明。

只要能证明用户身份,那么Token可以是任意形式,一段文本、一张图片甚至是一个硬件(比如银行的动态密码器)。在WEB服务中,Token多数情况以一个字符串的形式进行传输和存储。而载体,通常就是Cookie。
服务端使用Cookie机制,将Token发送给客户端,客户端在请求时使用Cookie携带Token,完成了身份的证明。

Token是拥有身份证明意义的内容,Cookie是HTTP协议中用来保持会话状态的一种机制。

Token是内容,可以基于Cookie传输,也可以不基于Cookie传输(Header、Query、Body, etc.);Cookie是载体,可以承载Token,也可以承载其他内容。

Cookie安全吗?

Cookie是一个协议,一种机制,Cookie机制依赖HTTP协议。安全性包含很多方面:

  1. 从窃听的角度来说,Cookie的安全性以来HTTP协议的安全性,使用HTTP(没有S)协议的请求,可以轻易被窃听,那么此时Cookie也是不安全的。
  2. 从内容的角度来说,客户端(浏览器)因为具有管理Cookie的职责,所以客户端可以“监守自盗”,获取Cookie轻而易举。
  3. 从隐私角度来说,Cookie只是一个载体,只有Cookie的内容涉及隐私时,Cookie才可能泄漏隐私。

为了保证安全性,针对以上三个角度的问题,可以有以下解决方案:

  1. 使用HTTPS协议,因为HTTPS协议难以被窃听,可以防止Cookie被盗取。
  2. 使用可靠的浏览器,避免浏览器(客户端)被恶意利用(本地的其他软件或服务,以及浏览器插件等等)。
  3. 避免在Cookie中保存隐私信息。

实例

一次完整的登录、登出流程可能是这样:

  1. 客户端第一次访问资源,此时不带有Cookie。服务器接收到请求后,创建会话并分配会话id,如:TOKEN。该会话暂时不包含任何内容。会话id将通过Cookie返回给客户端:SID=TOKEN。客户端收到响应并保存Cookie到本地。
  2. 客户端访问其他无需身份认证的资源,服务器可以识别为同一客户端。
  3. 客户端发起身份验证请求,提交用户名、密码到服务器。服务器验证成功,将用户信息存入SID=TOKEN的Session(假设用户id为001),此时Session内容为: {user_id:001}。
    1. 服务器还有其他选择,比如再创建一个Session,同时将新的Session id返回给客户端。
    2. 或者修改TOKEN的有效期。
    3. 只要服务器将用户信息关联到Session,同时将Session id返回给客户端,即可”保存登录状态“。
  4. 整个过程,客户端一直带有相同值的Cookie。
  5. 客户端有权限访问用户001的资源,服务端可以识别是001用户访问了某些资源。
  6. 客户端发起退出登录的请求。服务端收到请求后,关闭会话,清除Session,创建新的Session并将新的Session id返回给客户端。
    1. 服务器当然也有其他选择,比如只清除Session中的内容,但是不改变Session id,关键在于服务器已经关闭会话。

以上是我基于现有经验进行的整理,如有错误或其他观点,欢迎讨论。