揭秘 JWT:如何保护你的下一个 Web 应用

2025-05-27

揭秘 JWT:如何保护你的下一个 Web 应用

您如何保护您的 Web 应用程序?您使用会话 Cookie 吗?第三方身份验证?SAML 吗?今天我将向您介绍一个简洁的标准,称为 JSON Web Tokens,简称 JWT。如果您从事 Web 应用程序开发,您很可能听说过它,但今天我将尝试为您揭秘它。

如果你有兴趣了解所有细节,可以阅读 RFC,但这不是本文的目的。相反,我将:

  1. 给你一个 JWT 的高层概述
  2. 更深入地了解 JWT 的工作原理以及它的优点
  3. 介绍一些常见的 JWT 安全陷阱

什么是 JWT?

JSON Web Token (JWT) 是用于创建和传输数据的开放标准。它提供了一种方法,可以对 JSON 有效负载进行加密签名以验证其真实性和完整性,以及/或者对 JSON 有效负载进行加密以提供机密性。请注意,您有时可能会听到加密签名被称为数字签名——它们是同一事物的两个名称。

JWT 是经过加密签名的令牌

就本文而言,我们将讨论加密签名的令牌。加密签名的令牌由服务器颁发给用户,然后用户可以将其返回给服务器,以证明用户有权执行操作。这种加密签名有两个主要优点:

  1. 由于只有服务器知道密钥,因此只有服务器可以颁发有效的令牌。
  2. 由于加密签名的特性,修改或篡改令牌及其 JSON 负载不可能不被检测到。(想知道其工作原理?点击此处了解更多信息。)

这些属性使 JWT 成为一种很好的授权机制:当用户使用其用户名和密码登录时,您可以向他们发出一个令牌,其中包含识别信息,例如他们的用户 ID、他们的权限/访问级别以及其他可能有用的属性。

然后,当用户尝试访问应用程序路由或功能时,他们会将此令牌提供给服务器,服务器可以从令牌中读取这些属性。一旦应用程序确认令牌有效(令牌可以配置过期时间)且未被篡改,您就可以根据令牌中的信息做出授权决策。

令牌结构:JWT 的 3 个部分

签名的 JSON Web Token 有 3 个主要部分:标头、JSON有效负载签名

  1. 包含用于生成加密签名的加密算法的 JSON,还可以包含其他信息,例如令牌类型和 x.509 证书链信息(如果您正在使用它)。
  2. 有效负载是一个 JSON 对象。它包含的数据称为声明。JWT 标准定义了七个标准声明。您可以将它们视为“保留”声明,就像大多数编程语言中的某些关键字被保留以表示某些事物并且不能用作其他变量名一样(想到的示例包括class ifelse等等)。这些标准声明可以存储有关用户身份、到期信息、发行者等的信息。您还可以根据需要向令牌添加其他声明。我将在下面的小节中详细介绍这一点。
  3. 签名,其计算方法是先用 base64 编码标头和有效负载,然后将它们与 连接在一起.,最后使用服务器的私钥加密此字符串。为了验证令牌,服务器将对收到的令牌的标头和有效负载重复此过程,然后将结果与令牌的签名块进行比较。如果令牌已被篡改,则两者将不匹配。

为了将这些部分组成令牌,每个部分都经过base64 编码,然后各个部分之间用点号连接起来。以下是示例:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9.gzSraSYS8EXBxLN_oWnFSRgCzcmJmMjLiuyu5CSpyHI
Enter fullscreen mode Exit fullscreen mode

JWT 声明 - 将信息存储在 JWT 令牌中

JWT 的声明在令牌的有效负载中定义。它们可以存储有关令牌、颁发者、颁发给用户的有用信息,以及其他可选信息。

例如,当用户登录时,服务器会检查他们是否具有管理员权限,然后向用户发出一个包含其用户 ID 并说明他们是否具有管理员权限的令牌:

{
  "iat": 1609781109,
  "nbf": 1609781109,
  "jti": "0c2df7d5-f940-409a-b8b5-b3c6f9f1ef1e",
  "exp": 1609784709,
  "identity": "964403f4-444a-428a-88a0-15da8cdaf17c",
  "fresh": false,
  "type": "access",
  "user_claims": {
    "email": "example@example.com",
    "real_name": "John Doe",
    "customer_acct": "Some Organization LLC",
    "is_admin": true
  }
}
Enter fullscreen mode Exit fullscreen mode

在这种情况下,identity是作为用户标识符的 GUID。iatnbexpjti字段都是标准声明user_claims是我添加的用于存储有关用户的其他信息的声明。

当用户尝试执行某个操作时,服务器可以检查随用户请求发送的令牌,并可以使用这些声明来查看用户是否有权执行该操作。

在应用程序中使用 JWT 的好处

使用 JSON Web Tokens 有很多优点:

  • Web 运行在 JavaScript 之上,因此 JSON 是存储身份验证信息的绝佳选择。但 JWT 并不局限于 JavaScript 应用程序——从 PHP 到 Python 再到 Go,所有编程语言都可以使用 JSON。它灵活且易于使用。
  • JWT 声明允许您轻松存储有关用户的附加信息,您可以在应用程序中访问这些信息,而无需进行数据库查找。
  • Token 体积小且 URL 安全。它们可以存储为 Cookie、本地存储或会话存储。
  • 大多数常见的 Web 框架都提供了 JWT 库,可以帮你完成所有繁琐的工作。(我会在本文末尾附上一些库的链接)。

常见的 JWT 安全陷阱

与任何安全机制一样,JWT 也存在一些常见的陷阱。这些陷阱并不难避免,但你需要了解它们是什么才能避免它们:

JWT 用于授权,而不是身份验证

JWT 是一种授权机制,而非身份验证机制。两者的区别很重要:身份验证是确保用户的身份与其声明的身份相符。授权是确定用户是否被授权(允许)执行某个操作,通常是在身份验证完成后。

在向用户颁发 JWT 令牌之前,您应该对其进行身份验证——这通常通过用户名和密码来完成。(如果您想了解更多信息,请参阅我关于密码哈希的文章)。一旦用户通过身份验证(即他们的用户名和密码已通过验证),您就可以向他们颁发一个令牌,他们可以在后续向您的应用程序发出请求时使用该令牌进行授权。

确保您的密钥是安全的

如果您正在跟随演示进行操作,他们通常会提供包含示例代码的示例密钥。请勿复制他们的密钥,请自行生成。不要使用简短的单词或短语,而应使用较长的随机密钥。

不要将你的密钥硬编码到你的应用程序中

为了对令牌进行签名,您的服务器需要拥有一个可供其使用的密钥。根据您所使用的语言的 JWT 框架,您可以通过多种方式指定密钥。切勿将密钥硬编码到应用程序中。硬编码密钥会导致密钥被提交到版本控制中。(如果您的项目是公开的,这种情况尤其糟糕!)任何拥有密钥的人都可以创建令牌,因此务必保密。我建议使用环境变量或某种密钥管理器。

把 token 存储在 Cookie 里?这样安全吗?

确保在 JWT Cookie 上设置secure和属性。该属性将确保浏览器仅通过加密 ( ) 连接发送令牌,以防止 Cookie 被拦截。HttpOnlysecurehttps

HttpOnly属性将确保 cookie 不能通过 JavaScript 访问,这将有助于减轻跨站点脚本 (XSS) 攻击。

您可以在此处找到更多相关信息

结论

关键要点:

  • JWT 是一种开放标准,可以在用户通过身份验证后用于授权。
  • 如果不知道密钥,JWT 令牌就无法被伪造或修改(未经检测)。
  • JWT 允许您将 JSON 数据(“声明”)存储在可用于授权或其他目的的令牌中
  • JWT 易于使用,并且有很多优秀的框架可以在您的应用程序中实现它
  • 确保您的应用程序以安全的方式管理密钥和 JWT 令牌

希望这篇文章对你有用!请在下方评论区留言,分享你的想法。

如果您正在为云应用程序编写代码,那么当出现问题时,您需要立即采取行动。我参与构建了CodeLighthouse,它能够实时向开发人员发送应用程序错误通知,以便您更快地发现并修复错误。立即在codelighthouse.io免费开始使用!

脚注

正如承诺的那样,以下是一些适用于 Python/Flask、Node.js/Express 和 PHP 的 JWT 库的链接:

Flask-jwt-extended:Python Flask 框架的一个高度强大的模块,我非常喜欢使用它。

Express-jwt:一个很棒的软件包,可以无缝集成到 Node.js Express 应用中。如果你同时使用 Node.js 和 Express 进行构建,我强烈推荐它。

php-jwt:由 Firebase 维护的适用于 PHP 的高质量 JWT 库。

文章来源:https://dev.to/kmistele/demystifying-jwt-how-to-secure-your-next-web-app-9h0
PREV
开源 Tailwind UI 替代方案
NEXT
简历中十大 React 项目