我应该使用 JWT 作为身份验证令牌吗?
Should I use JWTs for authentication tokens?

原始链接: https://blog.ploetzli.ch/2024/should-i-use-jwt-for-authentication/

JSON WebToken (JWT) 是用于创建身份验证令牌的标准,由标头、负载和签名组成。 它们非常适合 Google 和 Facebook 等大型系统,在这些系统中,令牌验证不需要检查用户数据库。 这些令牌可以指示发行者、受众、过期时间和其他声明,而无需持续的数据库交互。 然而,由于智威汤逊针对大容量环境的设计,较小的系统面临着限制。 相反,请考虑直接使用现有数据库管理会话,并避免与 JWT 相关的复杂性。 例如,您可以发出不透明的会话令牌,对其进行验证,然后将相关信息存储在数据库中。 与 JWT 相比,这种方法简化了管理,无需处理密钥,并且可能会导致更少的问题。 仅在扩展到大规模级别并处理需要最少用户数据库调用的广泛交互时才选择 JWT。

一位用户认为,对于更简单的 Web 会话和 API 身份验证,存储在数据库中的传统会话令牌可以有效地工作。 他们建议在数据库前面放置一个缓存层以提高性能。 然而,他们承认在某些情况下需要撤销或禁用令牌,这使得 JWT 在这些情况下受益,因为它们的生命周期很长并且能够包含声明。 他们批评 JWT 与更简单的方法相比的复杂性和潜在的麻烦,但承认 JWT 有时在特定用例中是必要的。 用户分享了他们在 Twilio 工作十年的个人经验,其中用户和身份验证数据库的扩展问题需要比单令牌解决方案更先进的技术。 尽管承认 JWT 有其缺点,但它们仍然是管理复杂身份验证要求的宝贵工具。 用户提出了一种有趣的 JWT 撤销反向缓存策略,但对其实践中的有效性表示不确定。 他们强调了访问控制和实时注销的重要性,以防止入侵和不当使用被盗令牌。 总体而言,用户强调了解不同用例的不同身份验证策略的独特挑战和优势的重要性。
相关文章

原文

No.


Not satisfied? Fine, fine. I’ll write a longer answer.

Let’s talk about what we’re talking about. JWT stands for JSON Web Tokens, a reasonably well defined standard for authenticated tokens. Specifically they have a header with format information, a payload, and a signature or message authentication code. The core idea is that whoever has the corresponding verification key can verify that the payload is authentic and has not been altered. What they do with that information is up to them.

The JWT spec (RFC 7519) makes suggestions by providing a few well-known registered claim names: issuer, audience, subject, expiration time, etc. A common usage pattern is that, after verifying the authenticity against whatever trust relationship they have with the issuer, the recipient checks whether they are the intended audience (if any is specified) and the expiration time has not yet passed, and then take the subject as an authenticated identity of the bearer of the token.

It’s perfectly designed for bearer token authentication! Or is it? Let me be clear: JWT as authentication tokens are constructed for Google/Facebook scale environments, and absolutely no one who is not Google/Facebook needs to put up with the ensuing tradeoffs. If you process less than 10k requests per second, you’re not Google nor are you Facebook.

The core benefit, proponents will tell you, is that the recipient of a JWT doesn’t need to connect to the user database to verify the token authenticity and render its service. In a large installation, like Google’s, that means that the JWT issuer, the authentication service, can be a dedicated service that is managed and scaled like other services, and is the only service that needs to access the centralized user database. All other services can act on the information stored in the JWT alone, and don’t need to go through the user database, which would represent a choke point.

What about logout/session invalidation? Well, in order for this model to work, the authentication token should have a fairly short lifetime. Maybe 5 minutes, max. The client is also issued a second token, the so-called refresh token, with which it can request a new authentication token from the authentication service. This gives the authentication service a chance to consult the user database to see whether the user or a specific session has been blocked in the meantime.

Here’s the twist that is rarely, if ever, spelled out: In this setup the refresh token, not the authentication token, is the real session token. The refresh token represents the session with the authentication service (which can be revoked), while the authentication tokens are just derived credentials to be used for a few requests at most. The beauty, from Google’s point of view, is that this delegates keeping the session alive to the client, i.e. not Google’s servers. Oh and by the way, the refresh token can be, and usually is, opaque, since it’s only ever consumed by the same service that creates it. That reduces a lot of complexity, by just using an opaque identifier stored in a database.

Now, let’s assume you are not Google. Check which of these apply to you:

  • You wanted to implement log-out, so now you’re keeping an allowlist of valid JWTs, or a denylist of revoked JWTs. To check this you hit the database on each request.
  • You need to be able to block users entirely, so you check a “user active” flag in the database. You hit the database on each request.
  • You need additional relationships between the user object and other objects in the database. You hit the database on each request.
  • Your service does anything at all with data in the database. You hit the database on each request.

Congratulations, if you confirmed any of the items above, you don’t need JWTs. You’re hitting the database anyway, and I’m pretty sure that you only have one database which stores both your user profiles and your application data. By just using a “normal” opaque session token and storing it in the database, the same way Google does with the refresh token, and dropping all JWT authentication token nonsense, you stand to reap these great benefits:

  • No weird workarounds (allow/denylist) for shortcomings of JWT as authentication token
  • Greatly reduced complexity. No need to manage a secure JWT signing/authentication key
  • You get to pass on some interesting bugs.

Just use the normal session mechanism that comes with your web framework and that you were using before someone told you that Google uses JWT. It has stood the test of time and is probably fine.

If you need something to do to make you feel like you’re running a big deployment, you can probably configure your session mechanism to use redisvalkey to store the session data. You’re still going to use the authenticated user id to query the database, but for unauthenticated requests it may be faster/use less resources. It might not be. You’ll have to tune and measure that.

联系我们 contact @ memedata.com