前言
本文将首先概述基于cookie的身份验证方式和基于token的身份验证方式,在此基础上对两种验证进行比较。
最后将介绍JWT(主要是翻译官网介绍)。
概述
HTTP是一个“无状态”协议,这意味着Web应用程序服务器在响应客户端请求时不会将多个请求链接到任何一个客户端。然而,许多Web应用程序的安全和正常运行都取决于系统能够区分用户并识别用户及其权限。
这就需要一些机制来为一个HTTP请求提供状态。它们使站点能够在会话期间对各用户做出适当的响应,从而保持跟踪用户在应用程序中的活动(请求和响应)。
cookie和token
下面两图大致展示了基于cookie和基于token工作流程。
基于cookie的身份验证
cookie是源自站点并由浏览器存储在客户计算机上的简单文件。它们通常包含一个名称和一个值,用于将客户端标识为对站点具有特定许可权的特定用户。
cookie与源域相连接的方式可以确保仅源域能够访问其中存储的信息。第三方服务器既不能读取也不能更改用户计算机上该域的cookie内容。
网景公司的前雇员于1993年发明了cookie。
基于cookie的验证是有状态的,就是说验证或者会话信息必须同时在客户端和服务端保存。这个信息服务端一般在数据库中记录,而前端会保存在cookie中。
验证的一般流程如下:
- 用户输入登陆凭据;
- 服务器验证凭据是否正确,并创建会话,然后把会话数据存储在数据库中;
- 具有会话id的cookie被放置在用户浏览器中;
- 在后续请求中,服务器会根据数据库验证会话id,如果验证通过,则继续处理;
- 一旦用户登出,服务端和客户端同时销毁该会话。
基于token的身份验证
随着单页面应用程序的流行,以及Web API和物联网的兴起,基于token的身份机制越来越被大家广泛采用。
当讨论基于token的身份验证时,一般都是说的JSON Web Tokens(JWT)。虽然有着很多不同的方式实现token,但是JWT已经成为了事实上的标准,所以后面会将JWT和token混用。
基于token的验证是无状态的。服务器不记录哪些用户已登陆或者已经发布了哪些JWT。对服务器的每个请求都需要带上验证请求的token。该标记既可以加在header中,可以在POST请求的主体中发送,也可以作为查询参数发送。
工作流程如下:
- 用户输入登陆凭据;
- 服务器验证凭据是否正确,然后返回一个经过签名的token;
- 客户端负责存储token,可以存在local storage,或者cookie中;
- 对服务器的请求带上这个token;
- 服务器对JWT进行解码,如果token有效,则处理该请求;
- 一旦用户登出,客户端销毁token。
token相对cookie的优势
无状态
基于token的验证是无状态的,这也许是它相对cookie来说最大的优点。后端服务不需要记录token。每个令牌都是独立的,包括检查其有效性所需的所有数据,并通过声明传达用户信息。
服务器唯一的工作就是在成功的登陆请求上签署token,并验证传入的token是否有效。
防跨站请求伪造(CSRF)
举个CSRF攻击的例子,在网页中有这样的一个链接
![](http://bank.com?withdraw=1000&to=tom)
,假设你已经通过银行的验证并且cookie中存在验证信息,同时银行网站没有CSRF保护。一旦用户点了这个图片,就很有可能从银行向tom这个人转1000块钱。
但是如果银行网站使用了token作为验证手段,攻击者将无法通过上面的链接转走你的钱。(因为攻击者无法获取正确的token)
多站点使用
cookie绑定到单个域。foo.com域产生的cookie无法被bar.com域读取。使用token就没有这样的问题。这对于需要向多个服务获取授权的单页面应用程序尤其有用。
使用token,使得用从myapp.com获取的授权向myservice1.com和myservice2.com获取服务成为可能。
支持移动平台
好的API可以同时支持浏览器,iOS和Android等移动平台。然而,在移动平台上,cookie是不被支持的。
性能
一次网络往返时间(通过数据库查询session信息)总比做一次HMACSHA256计算的Token验证和解析要费时得多。
JWT
JWT是JSON Web Token的缩写。它定义了一种紧凑且独立的方式,用于将各方之间的信息安全地传输为JSON对象。这是一个开放的标准,见RFC 7519。
基于JWT的信息可以通过数字签名进行校验。校验的方法即可以使用消息摘要(HMAC),或者非对称加密(RSA)。
JWT具有两个特点:
- 紧凑。由于其较小的尺寸,JWT可以通过URL,POST参数或者HTTP头发送。较小的尺寸会带来传输速度的优势;
- 自包含:token中包含了用户的所有必须信息,避免了多次查询数据库的需要。
应用场景
以下是JWT有用的一些场景
- 验证:这是JWT最常用的场景。一旦用户登陆成功,每个后续的请求将包括JWT,服务器在对JWT进行验证后,允许用户访问服务和资源。单点登陆是一个广泛使用JWT的场景,因为它的开销相对较小,并且能够在不同的域中轻松使用。
- 信息交换:JWT是在可以安全地传输信息。因为JWT可以被签名,收信人可以确认发信人的身份,同时也能够验证内容是否被篡改。
格式
JWT包括三个部分:头部、载荷和签名,这三个部分通过.
连接起来。
因此,一个典型的JWT长这样xxxxx.yyyyy.zzzzz
。
头部
头部通常包括两部分:token类型(JWT),和使用到的算法,如HMAC、SHA256或RSA,下面是一个例子,说明这是一个JWT,使用的签名算法是HS256。
{
"alg": "HS256",
"typ": "JWT"
}
头部会通过Base64Url编码形成JWT的第一部分
载荷
第二部分是载荷,要传递出去的声明,其中包含了实体(通常是用户)和附加元数据。有三种类型的声明:
- 保留声明:这是一组预定义的声明,非强制性,用来帮助接收方(服务器)更好地理解这个JWT。其中包括:iss(issuer,该JWT的签发者),exp(expiration time,过期时间),sub(subject,该JWT所面向的用户),aud(audience,JWT的接收者),和另外一些声明
- 公共声明:这些可以用使用JWT的人随意定义。但是为了避免冲突,应在在IANA JSON WEB令牌注册表中定义它们,或者将其定义为包含防冲突命名空间的URI。
- 私有声明:这些是为了在同意使用它们的各方之间共享信息而创建的自定义声明。
下面是一个例子
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
载荷会通过Base64Url编码形成JWT的第二部分
签名
将上面两部分编码后,使用.
连接在一起,形成了xxxxx.yyyyyy。
最后,采用头部指定的算法,和私钥对上面的字符串进行签名。
加入采用的是HMAC SHA256 算法,签名将通过下面的方式生成
HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
该签名用户验证JWT发送者的身份,并确保该消息没有被篡改。
JWT工作流程
在身份验证过程中,一旦用户使用其凭据成功登陆,服务器将返回JWT,该JWT必须在客户端本地保存。这和服务器创建会话并返回cookie的传统方法不同。
每次用户要请求受保护的资源时,必须在请求中带上JWT。通常在Authorization头Bearer中,如下:
Authorization: Bearer <token>
这是一种无状态的认证机制,因为用户状态永远不会保存在服务器内存中。服务器的受保护路由将在授权头中检查有效的JWT,如果存在,则允许用户访问受保护的资源。由于JWT是自说明的,包含了所有必要的信息,这就减少了多次查询数据库的需要。
这样可以完全依赖无状态的数据API,甚至可以向下游服务发出请求。API的作用域并不重要,因此跨源资源共享(CORS)不会是一个问题,因为它不使用Cookie。
整个流程如下图:
使用JWT的理由
现在来谈谈JWT与简单网页令牌(SWT)和安全断言标记语言令牌(SAML)相比的优势。
由于JSON比XML更短小,编码时其大小也较小,使得JWT比SAML更紧凑。这使得JWT成为在HTML和HTTP环境中能更快地传递。
从安全角度来说,SWT只能通过使用HMAC算法的共享密钥进行对称签名。但是,JWT和SAML令牌可以以X.509证书的形式使用公钥/私钥对进行签名。与简单的JSON签名相比,使用XML数字签名签名XML而不引入模糊的安全漏洞是非常困难的。
JSON解析器在大多数编程语言中很常见,因为它们直接映射到对象。相反,XML没有自然的文档对对象映射。这使得使用JWT比SAML断言更容易。
从使用平台来说,JWT在Internet规模上使用。这突出了客户端处理多个平台上特别是移动平台上的JSON Web令牌的便利性。