本文将首先概述基于cookie的身份验证方式和基于token的身份验证方式,在此基础上对两种验证进行比较。 最后将介绍JWT(主要是翻译官网介绍)。
HTTP是一个“无状态”协议,这意味着Web应用程序服务器在响应客户端请求时不会将多个请求链接到任何一个客户端。然而,许多Web应用程序的安全和正常运行都取决于系统能够区分用户并识别用户及其权限。
这就需要一些机制来为一个HTTP请求提供状态。它们使站点能够在会话期间对各用户做出适当的响应,从而保持跟踪用户在应用程序中的活动(请求和响应)。
下面两图大致展示了基于cookie和基于token工作流程。
cookie是源自站点并由浏览器存储在客户计算机上的简单文件。它们通常包含一个名称和一个值,用于将客户端标识为对站点具有特定许可权的特定用户。
cookie与源域相连接的方式可以确保仅源域能够访问其中存储的信息。第三方服务器既不能读取也不能更改用户计算机上该域的cookie内容。
网景公司的前雇员于1993年发明了cookie。
基于cookie的验证是有状态的,就是说验证或者会话信息必须同时在客户端和服务端保存。这个信息服务端一般在数据库中记录,而前端会保存在cookie中。
验证的一般流程如下:
随着单页面应用程序的流行,以及Web API和物联网的兴起,基于token的身份机制越来越被大家广泛采用。
当讨论基于token的身份验证时,一般都是说的JSON Web Tokens(JWT)。虽然有着很多不同的方式实现token,但是JWT已经成为了事实上的标准,所以后面会将JWT和token混用。
基于token的验证是无状态的。服务器不记录哪些用户已登陆或者已经发布了哪些JWT。对服务器的每个请求都需要带上验证请求的token。该标记既可以加在header中,可以在POST请求的主体中发送,也可以作为查询参数发送。
工作流程如下:
基于token的验证是无状态的,这也许是它相对cookie来说最大的优点。后端服务不需要记录token。每个令牌都是独立的,包括检查其有效性所需的所有数据,并通过声明传达用户信息。
服务器唯一的工作就是在成功的登陆请求上签署token,并验证传入的token是否有效。
举个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是JSON Web Token的缩写。它定义了一种紧凑且独立的方式,用于将各方之间的信息安全地传输为JSON对象。这是一个开放的标准,见RFC 7519。
基于JWT的信息可以通过数字签名进行校验。校验的方法即可以使用消息摘要(HMAC),或者非对称加密(RSA)。
JWT具有两个特点:
以下是JWT有用的一些场景
JWT包括三个部分:头部、载荷和签名,这三个部分通过.
连接起来。
因此,一个典型的JWT长这样xxxxx.yyyyy.zzzzz
。
头部通常包括两部分:token类型(JWT),和使用到的算法,如HMAC、SHA256或RSA,下面是一个例子,说明这是一个JWT,使用的签名算法是HS256。
{
"alg": "HS256",
"typ": "JWT"
}
头部会通过Base64Url编码形成JWT的第一部分
第二部分是载荷,要传递出去的声明,其中包含了实体(通常是用户)和附加元数据。有三种类型的声明:
下面是一个例子
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
载荷会通过Base64Url编码形成JWT的第二部分
将上面两部分编码后,使用.
连接在一起,形成了xxxxx.yyyyyy。
最后,采用头部指定的算法,和私钥对上面的字符串进行签名。
加入采用的是HMAC SHA256 算法,签名将通过下面的方式生成
HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
该签名用户验证JWT发送者的身份,并确保该消息没有被篡改。
在身份验证过程中,一旦用户使用其凭据成功登陆,服务器将返回JWT,该JWT必须在客户端本地保存。这和服务器创建会话并返回cookie的传统方法不同。
每次用户要请求受保护的资源时,必须在请求中带上JWT。通常在Authorization头Bearer中,如下:
Authorization: Bearer <token>
这是一种无状态的认证机制,因为用户状态永远不会保存在服务器内存中。服务器的受保护路由将在授权头中检查有效的JWT,如果存在,则允许用户访问受保护的资源。由于JWT是自说明的,包含了所有必要的信息,这就减少了多次查询数据库的需要。
这样可以完全依赖无状态的数据API,甚至可以向下游服务发出请求。API的作用域并不重要,因此跨源资源共享(CORS)不会是一个问题,因为它不使用Cookie。
整个流程如下图:
现在来谈谈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令牌的便利性。