【应用安全 】JWT初学者入门指南

令牌身份验证,OAuth或JSON Web令牌的新手? 这是一个很好的起点!

首先,什么是JSON Web令牌,或JWT(发音为“jot”)? 简而言之,JWT是用于令牌认证的安全且值得信赖的标准。 JWT允许您使用签名对信息(称为声明)进行数字签名,并且可以在以后使用秘密签名密钥进行验证。

[安全 】JWT初学者入门指南

 

什么是令牌认证?

应用程序确认用户身份的过程称为身份验证。传统上,应用程序通过会话cookie保持身份,这些cookie依赖于服务器端存储的会话ID。在此结构中,开发人员被迫创建独特且特定于服务器的会话存储,或实现为完全独立的会话存储层。

令牌认证是一种更现代的方法,设计解决了服务器端会话ID无法解决的问题。使用令牌代替会话ID可以降低服务器负载,简化权限管理,并提供更好的工具来支持分布式或基于云的基础架构。在此方法中,为用户提供可验证凭据后会生成令牌。初始身份验证可以是用户名/密码凭据,API密钥,甚至来自其他服务的令牌。 (Stormpath的API密钥身份验证功能就是一个例子。)

有兴趣了解更多?查看此博客文章,了解如何使用令牌扩展用户管理或完整的产品文档。

JWT的剖析

如果您在野外遇到JWT,您会注意到它分为三个部分,标题,有效负载和签名。 (随着我们剖析JWT的解剖结构,请关注Stormpath的开源Java JWT工具!)以下是典型JWT的示例:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
.
eyJzdWIiOiJ1c2Vycy9Uek1Vb2NNRjRwIiwibmFtZSI6IlJvYmVydCBUb2tlbiBNYW4iLCJzY29wZSI6InNlbGYgZ3JvdXBzL2FkbWlucyIsImV4cCI6IjEzMDA4MTkzODAifQ
.
1pVOLQduFWW3muii1LExVBt2TK1-MdRI4QjhKryaDwc

在此示例中,第1节是描述令牌的标头。 第2节是有效载荷,其中包含JWT的声明,第3节是签名散列,可用于验证令牌的完整性(如果您有用于签名的密钥)。

当我们解码有效载荷时,我们得到这个包含JWS声明的漂亮,整洁的JSON对象:

{
 "sub": "users/TzMUocMF4p",
 "name": "Robert Token Man",
 "scope": "self groups/admins",
 "exp": "1300819380"
}

要求(Cliam) 告诉你,至少:

  • 这个人是谁以及他们的用户资源的URI(子要求)
  • 此人可使用此令牌访问的内容(范围声明)
  • 令牌过期时您的API应在验证令牌时使用此功能。

因为令牌是使用密钥签名的,所以您可以验证其签名并隐含地信任所声称的内容。

JWE,JWS和JWT

根据JWT规范,“JWT将一组声明表示为以JWS和/或JWE结构编码的JSON对象。”术语“JWT”在技术上仅描述了无符号标记;我们称之为JWT的通常是JWS或JWS + JWE。

JWS - JSON Web签名

在JWS方案中,服务器对JWT进行签名并使用签名将其发送到客户端。签名保证了JWT要求没有被伪造或篡改。但是,JWT未加密(内容基本上是纯文本)。

JWE - JSON Web加密

另一方面,JWE方案在不签名的情况下加密内容。这为您的JWT带来了机密性,但不是JWE签名和封装JWE的安全性。

什么是OAuth?

OAuth 2.0是与可以委派身份验证或提供授权的服务进行交互的框架。它被广泛用于许多移动和Web应用程序。 OAuth 2.0没有指定令牌格式,但JWT正在迅速成为业界的事实标准。

在OAuth范例中,有两种令牌类型:访问和刷新令牌。首次进行身份验证时,通常会为您的应用程序(以及您的用户)提供两个令牌,但访问令牌设置为在短时间后过期(此持续时间可在应用程序中配置)。初始访问令牌到期后,刷新令牌将允许您的应用程序获取新的访问令牌。刷新令牌具有设置的到期时间,允许无限制地使用,直到达到该到期点。 Access和Refresh Tokens都具有内置安全性(签名时)以防止篡改,并且仅在特定持续时间内有效。

Stormpath使用OAuth,因为它是一个行业标准,任何兼容的库都可以利用它。 Stormpath目前支持三种OAuth的授权类型:

  • 密码授予类型:提供基于用户名和密码获取访问令牌的功能
  • 刷新授权类型:提供基于特殊刷新令牌生成另一个访问令牌的功能
  • 客户端凭据授权类型:提供为访问令牌交换API密钥对的功能。这通过API密钥管理功能得到支持

用Java创建和验证JWT

所以,你在代币上出售,现在,你如何在你的应用程序中使用它们?

好吧,如果你是Java开发人员,你应该从JJWT开始。 JJWT是一个Java库,提供由我们自己的Les Hazlewood开发并由开发人员社区维护的端到端JSON Web令牌创建和验证。永远免费和开源(Apache许可证,版本2.0),它设计了一个以构建者为中心的界面,隐藏了其大部分复杂性。

创建

由于JJWT的流畅界面,JWT的创建基本上分为三个步骤:

令牌的内部声明的定义,如Issuer,Subject,Expiration和ID。

密码签名JWT(制作JWS)

根据JWT Compact Serialization规则,将JWT压缩为URL安全字符串

最终的JWT将是一个由三部分组成的Base64编码字符串,使用提供的密钥使用指定的签名算法进行签名。在此之后,令牌已准备好与另一方共享。

这是使用JJWT库从上面创建JWT的示例:

String jwt = Jwts.builder()
 .setSubject("users/TzMUocMF4p")
 .setExpiration(new Date(1300819380))
 .claim("name", "Robert Token Man")
 .claim("scope", "self groups/admins")
 .signWith(
 SignatureAlgorithm.HS256,
 "secret".getBytes("UTF-8")
 )
 .compact();

证实

一旦拥有JWT,您通常会将其交还给请求它的客户端。 然后,客户端将其存储并将请求中的令牌传递给您的应用程序。 这通常使用HTTP中的cookie值或授权标头来完成。 例如:

HTTP/1.1
 
GET /secure-resource
 
Host: https://yourapplication.com
 
Authorization: Bearer eyJraWQiOiIzMUUzRDZaM0xaMVdFSEJGWVRQRksxRzY4IiwiYWxnIjoiSFMyNTYifQ.eyJqdGkiOiI2a3NjVFMyUjZuYlU3c1RhZ0h0aWFXIiwiaWF0IjoxNDQ1ODU0Njk0LCJpc3MiOiJodHRwczovL2FwaS5zdG9ybXBhdGguY29tL3YxL2FwcGxpY2F0aW9ucy8zUUlNbEpLS04yd2hHQ1l6WFh3MXQ4Iiwic3ViIjoiaHR0cHM6Ly9hcGkuc3Rvcm1wYXRoLmNvbS92MS9hY2NvdW50cy8xeG15U0dLMXB5VVc1c25qOENvcmU1IiwiZXhwIjoxNDQ1ODU4Mjk0LCJydGkiOiI2a3NjVE9pTUNESVZWM05qVTIyUnlTIn0.VJyMOicMOdcOCtytsx4hoPHy3Hl3AfGNfi2ydy8AmG4

验证JWT允许您验证其真实性(通过检查其数字签名,您可以检查它是否已过期并验证它是否未被篡改)并获取有关发送令牌的用户的信息。

以下是验证我们在上面创建的JWT的示例:

String jwt = <jwt passed in from above>
Jws<Claims> claims = Jwts.parser()
 .setSigningKey("secret".getBytes("UTF-8"))
 .parseClaimsJws(jwt)
String scope = claims.getBody().get("scope")
assertEquals(scope, "self groups/admins");

如果签名不正确,则对parseClaimsJws的调用将抛出SignatureException。成功解析后,可以获取并检查单个声明,如下所示:String scope = claims.getBody()。get(“scope”)。

例外

JJWT在与JWT合作时进行了各种验证。所有与JJWT相关的异常都是RuntimeExceptions,以JwtException作为基类。

这些错误会导致抛出特定异常:

  • ClaimJwtException:在验证JWT声明失败后抛出
  • ExpiredJwtException:表示JWT在过期后被接受,必须被拒绝
  • MalformedJwtException:当JWT未正确构造并且应该被拒绝时抛出
  • PrematureJwtException:表示JWT在被允许访问之前被接受,必须被拒绝
  • SignatureException:表示计算签名或验证JWT的现有签名失败
  • UnsupportedJwtException:在接收到与应用程序预期格式不匹配的特定格式/配置的JWT时抛出。例如,如果在应用程序需要加密签名的声明JWS时解析无符号明文JWT,则会抛出此异常
  • JJWT使用了许多其他Exception类。它们都可以在JJWT源代码中的io.jsonwebtoken包中找到。

令牌安全吗?

这里真正的问题是,你安全地使用它们吗?在Stormpath,我们遵循这些最佳实践,并鼓励我们的客户也这样做:

  • 将您的JWT存储在安全的HttpOnly cookie中。这可以防止跨站点脚本(XSS)攻击。
  • 如果您使用cookie来传输JWT,CSRF保护非常重要!未经用户同意,向您的网站提出请求的其他域名可能会恶意使用您的Cookie。如果您的服务器盲目地对用户进行身份验证,只是因为他们有cookie,那么您遇到的问题比硬盘驱动器大。您还允许进行CSRF攻击,其他网站会在未经用户同意的情况下触发您服务器上的状态更改操作。这是可能的,因为浏览器将始终自动发送用户的cookie,无论请求是如何被触发的。使用众多CSRF预防措施之一来降低此风险。
  • 使用仅可用于身份验证服务的强密钥对您的令牌进行签名。每次使用令牌对用户进行身份验证时,您的服务器必须验证令牌是否已使用您的密钥签名。
  • 不要将任何敏感数据存储在JWT中。这些令牌通常被签名以防止操纵(未加密),因此可以容易地解码和读取权利要求中的数据。如果您必须在其中放入敏感的,不透明的信息,请加密您的令牌。秘密签名密钥只能由发行方和消费者访问;它不应该在这两方之外进行。
  • 如果您担心重播攻击,请在声明中包含nonce(jti声明),到期时间(exp声明)和创建时间(ifat声明)。这些在JWT规范中有明确定义。

JJWT,JSONWebToken.io和JWT Inspector

Stormpath支持开发几个与JWT相关的开源开发人员工具,包括:

JJWT

JJWT是一个易于使用的工具,供开发人员用Java创建和验证JWT。在Stormpath支持的许多库中,JJWT是完全免费和开源的(Apache License,Version 2.0),因此每个人都可以看到它的作用以及它是如何做到的。不要犹豫,报告任何问题,建议改进,甚至提交一些代码!

JSONWebToken.io

JSONwebtoken.io是我们创建的一个开发工具,可以轻松解码JWT。将现有JWT简单粘贴到适当的字段中以解码其标头,有效负载和签名。 JSONWebToken.io由nJWT提供支持,nJWT是Node.js开发人员最干净的免费和开源(Apache License,Version 2.0)JWT库。

JWT检查器

JWT Inspector是一个开源的Chrome扩展程序,允许开发人员直接在浏览器中检查和调试JWT。 JWT Inspector将在您的站点上发现JWT(在cookie,本地/会话存储和标题中),并通过导航栏和DevTools面板轻松访问它们。

想要了解有关JWT,令牌认证或用户身份管理的更多信息?以下是我们团队的一些进一步资源:

  • 单页应用程序的令牌认证
  • 使用Spring Boot和Stormpath进行OAuth令牌管理
  • Java应用程序的令牌认证
  • 使用JSON Web令牌构建安全的用户界面
  • OAuth不是单点登录