我们所有人都知道如果攻击者发现我们的用户凭据(电子邮件和密码)会发生什么:他们可以登录我们的帐户并造成严重破坏。但是很多现代应用程序都在使用JSON Web令牌(JWT)来管理用户会话 - 如果JWT被泄露会发生什么?由于越来越多的应用程序正在使用基于令牌的身份验证,因此这个问题与开发人员越来越相关,并且对于了解是否构建使用基于令牌的身份验证的任何类型的应用程序至关重要。
为了帮助完整地解释这些概念,我将向您介绍令牌是什么,它们如何被使用以及当它们被盗时会发生什么。最后:如果你的令牌被盗,我会介绍你应该做什么,以及如何在将来防止这种情况。
这篇文章的灵感来自StackOverflow这个问题。我对这个问题的回答已成为我迄今为止对StackOverflow最受欢迎的回复之一!
Web开发上下文中的标记只不过是表示会话的任意值。标记可以是“abc123”之类的字符串,也可以是随机生成的ID,如“48ff796e-8c8a-46b9-9f25-f883c14734ea”。
令牌的目的是帮助服务器记住某人是谁。以API服务为例:如果您有一个API密钥,可以让您通过服务器端应用程序与API服务进行通信,那么API密钥就是API服务用来“记住”您的身份的密钥,请查看您的帐户详细信息 ,并允许(或禁止)您提出请求。在此示例中,您的API密钥是您的“令牌”,它允许您访问API。
然而,当大多数人今天谈论令牌时,他们实际上是指JWT(无论好坏)。
JSON Web令牌是特殊类型的令牌,其结构使得它们便于在Web上使用。他们有一些定义特征:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlJhbmRhbGwgRGVnZ2VzIiwiaWF0IjoxNTE2MjM5MDIyfQ.sNMELyC8ohN8WF_WRnRtdHMItOVizcscPiWsQJX9hmw
因为JWT只是URL安全字符串,所以它们很容易通过URL参数等传递。
这是一个小代码片段,它使用njwt库在JavaScript中创建和验证JWT。这个例子纯粹是为了让您一眼就能看到如何创建JWT,在其中嵌入一些JSON数据并验证它。
const njwt = require("njwt");
const secureRandom = require("secure-random");
// This is a "secret key" that the creator of the JWT must keep private.
var key = secureRandom(256, { type: "Buffer" });
// This is the JSON data embedded in the token.
var claims = {
iss: "https://api.com",
sub: "someuserid",
scope: "freeUser",
favoriteColor: "black"
};
// Create a JWT
var jwt = njwt.create(claims, key);
// Log the JWT
console.log(jwt);
// Jwt {
// header: JwtHeader { typ: 'JWT', alg: 'HS256' },
// body:
// JwtBody {
// iss: 'https://api.com',
// sub: 'someuserid',
// scope: 'freeUser',
// favoriteColor: 'black',
// jti: '903c5447-ebfd-43e8-8f4d-b7cc5922f5ec',
// iat: 1528824349,
// exp: 1528827949 },
// signingKey: <Buffer 9c e9 48 a7 b3 c9 87 be 5f 59 90 a5 08 02 9b 98 5c 5e 1c 29 3f b0 33 c5 8c c8 f9 c8 3e 35 f0 7c 20 a0 aa 65 cc 98 47 b6 31 c5 5c d6 4e 6e 25 29 2b d3 ... > }
// The JWT in compacted form (ready for sending over the network)
var token = jwt.compact();
// Log the compacted JWT
console.log(jwt.compact());
// eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL2FwaS5jb20iLCJzdWIiOiJzb21ldXNlcmlkIiwic2NvcGUiOiJmcmVlVXNlciIsImZhdm9yaXRlQ29sb3IiOiJibGFjayIsImp0aSI6IjkwM2M1NDQ3LWViZmQtNDNlOC04ZjRkLWI3Y2M1OTIyZjVlYyIsImlhdCI6MTUyODgyNDM0OSwiZXhwIjoxNTI4ODI3OTQ5fQ.y7ad-nUsHAkI8a5bixYnr_v0vStRqnzsT4bbWGAM2vw
// Verify the JWT using the secret key
njwt.verify(token, key, (err, verifiedJwt) => {
if (err) throw err;
console.log("The JWT has been verified and can be trusted!");
// The JWT has been verified and can be trusted!
});
JWT通常用作Web应用程序,移动应用程序和API服务的会话标识符。但是,与传统会话标识符不同,传统会话标识符只是指向服务器端实际用户数据的指针,JWT通常直接包含用户数据。
JWT近年来变得流行的主要原因(自2014年以来仅存在)是它们可以包含任意JSON数据。JWT相对于传统会话ID的好处是:
因为JWT是无状态的,所以当服务器端应用程序收到JWT时,它可以仅使用用于创建它的“密钥”来验证它 - 从而避免与后端数据库或缓存通信的性能损失,增加每个请求的延迟。
话虽如此,让我们来看看JWT通常如何在现代Web应用程序中使用。
简而言之:JWT用于识别客户端。就客户而言,它们是王国的关键。
简而言之:它很糟糕,真的很糟糕。
由于JWT用于识别客户端,如果其中一个被盗或受到攻击,攻击者可以完全访问用户的帐户,就像攻击者破坏用户的用户名和密码一样。
例如,如果攻击者获得了您的JWT,他们可以开始向服务器发送请求,将自己标识为您,并执行诸如进行服务更改,用户帐户更新等操作。一旦攻击者拥有您的JWT,就会结束游戏。
但是,有一件事使得被盗的JWT比被盗的用户名和密码稍微不那么糟糕:时机。由于JWT可以配置为在设定的时间(一分钟,一小时,一天等)后自动过期,因此攻击者只能使用您的JWT访问该服务,直到它过期。
从理论上讲,这听起来很棒,对吗?据称令牌认证的一种方式是使认证更加“安全”,这是通过短期令牌实现的。这是近年来基于令牌的身份验证真正起步的核心原因之一:您可以自动使令牌过期并降低依赖永久缓存的“无状态”令牌的风险。
毕竟,在安全领域,依靠缓存数据做出敏感决策,例如谁可以登录服务以及他们可以做什么被认为是一件坏事。因为令牌是无状态的并且允许比传统会话认证有一些速度改进,所以它们保持某种程度上“安全”的唯一方式是限制它们的寿命,以便它们在受到危害时不会造成太大的伤害。
这里唯一的问题是,如果攻击者首先能够窃取您的令牌,那么一旦获得新令牌,他们很可能会这样做。这种情况最常见的方式是通过中间人(MITM)连接或直接访问客户端或服务器。不幸的是,在这些情况下,即使是最短寿命的JWT也根本无法帮助你。
通常,令牌应被视为密码并受到保护。它们永远不应公开共享,并应保存在安全的数据存储中。对于基于浏览器的应用程序,这意味着永远不会将您的令牌存储在HTML5本地存储中,而是将令牌存储在JavaScript无法访问的服务器端cookie中。
通常,基于令牌的身份验证不会提供依赖于不透明会话标识符的典型基于会话的身份验证的任何额外安全性。虽然基于令牌的身份验证肯定有很多用例,但了解技术的工作原理以及弱点的位置至关重要。
另一个有趣的事情是,在某些情况下,被盗的JWT实际上可能比被盗的用户名和密码更糟糕。
让我们暂时假装您的用户名和密码已被盗用。在这种情况下,如果您登录的应用程序受多因素身份验证保护,则攻击者需要绕过其他身份验证机制才能访问您的帐户。
虽然猜测或暴力破解用户名和密码是一个非常现实的场景,但是能够危及用户的多因素身份验证设置可能非常困难。绕过基于应用程序的授权,短信验证,面部识别码,触摸ID等因素比猜测用户密码更具挑战性。
因此,受损的JWT实际上可能比受损的用户名和密码具有更大的安全风险。想象一下上面的场景,用户登录的应用程序受多因素身份验证的保护。一旦用户通过多因素登录并验证自己,就会为他们分配一个JWT来证明他们是谁。如果JWT被盗,攻击者不再需要直接绕过MFA(就像他们只有用户的用户名和密码一样) - 他们现在可以直接向用户发出请求而无需额外的身份证明。相当大的风险。
一旦JWT被盗,您将陷入困境:攻击者现在可以冒充客户并在未经客户同意的情况下访问您的服务。但是,即使你处境糟糕,你仍然需要充分利用它。
如果客户的令牌被盗,可以采取以下步骤。这些建议不适用于所有类型的应用,但应为您提供一些好主意,以帮助您从此安全事件中恢复:
一旦完成了这些步骤,您应该更好地了解令牌是如何被泄露的,以及需要采取哪些措施来防止令牌在未来发生。
当令牌妥协确实发生时,它可能会导致重大问题。特别是如果您(作为服务提供商)无法快速检测到攻击者已经破坏了客户端的令牌。
如果您能够自动识别令牌被泄露的情况怎么办?这将极大地提高您服务的安全性,因为您可以主动防止可疑请求得到满足,从而保护您的服务和用户。
虽然不容易,但这绝对是可能的。像TensorFlow这样的现代机器学习工具包允许您构建功能(虽然复杂)的管道,以检测异常模式并主动负责这种情况。
例如,您可以使用机器学习来检测不寻常的客户端位置。假设您运行一个网站,并且您的用户已从旧金山登录并且已经提出了几个小时的请求。如果您发现请求在短时间内开始来自不同的地理区域,您可以立即阻止这些请求被执行,撤消令牌,并联系用户以重置其密码等。
以类似的方式,您可以使用机器学习来检测异常的客户端行为。如果令牌遭到入侵,攻击者很可能会采取措施以某种方式滥用您的服务。如果您的用户通常在您的网站上每分钟发出五个请求,但突然之间您会注意到用户每分钟发出50多个请求的大幅提升,这可能是攻击者获得保留的良好指标用户的令牌,因此您可以撤消令牌并联系用户以重置其密码。
通过机器学习进行模式检测和识别是处理这些更复杂问题的一种奇妙的现代方法。
这正是我们在Okta所做的 - 我们运行一个API服务,允许您在我们的服务中存储用户帐户,我们提供开发人员库来处理身份验证,授权,社交登录,单点登录,多因素等事务当用户登录由Okta提供支持的应用程序时,我们会分析一些数据点以检测帐户是否已被盗用,提示进行多因素身份验证,执行用户外展等。
积极主动地保护您的安全性有很多复杂性,但准备比准备好要好得多。