【问题标题】:Is it safe to store sensitive data in JWT Payload?在 JWT Payload 中存储敏感数据是否安全?
【发布时间】:2017-09-15 17:56:35
【问题描述】:

我目前正在学习使用 PHP 实现 JWT,并希望在我的 RESTful 应用程序中使用 JWT 令牌而不是会话。

在签名创建期间,我正在做这样的事情

token = base64Header + '.' + base64Payload + '.' + signature  

这里我们只是使用 base64 作为 Payload。如果我粘贴到像 https://jwt.io/#debugger 这样的网站,Payload 会被解密(即使签名错误)。

我的问题,

  1. 发送数据时JWT是否仅用于验证与服务器的签名?
  2. 在 Payload 中保存敏感数据是否不安全?
  3. 如果不安全,有什么方法可以保护 Payload?

下面是我写的示例代码

<?php
    $headers = base64_encode(json_encode([
        "typ" => "JWT",
        "alg" => "HS256"
    ]));
    $claims = base64_encode(json_encode([
        "sub" => "1234567890",
        "name" => "John Doe",
        "admin" => true,
        "jti" => "870a3de5-ea7b-4062-abef-11180e530f5a",
        "iat" => 1492603378,
        "exp" => 1492606978
    ]));
    $payload = $headers.".".$claims;
    $signature = base64_encode(hash_hmac("sha256", $payload, 'secret', true));
    $encodedJWT = $payload.".".$signature;
    // eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImp0aSI6Ijg3MGEzZGU1LWVhN2ItNDA2Mi1hYmVmLTExMTgwZTUzMGY1YSIsImlhdCI6MTQ5MjYwMzM3OCwiZXhwIjoxNDkyNjA2OTc4fQ.nvw-bAUgr7H_xr3q8_Yz8rCNtMohtn2YlCmcLoLBWlc

【问题讨论】:

  • 请在投票前提供 cmets。如果有什么不对,我会更新我的问题:)
  • 嗨,根据我在 JWT 上参加的会议的信息,敏感数据应该保留在数据库中,令牌本身应该只包含快速识别所需的信息,而不是授权。
  • 我们应该在投票前提供 cmets 吗? -:) 投反对票很少的原因是因为 SO 版主建议不要因为偶尔的报复投反对票。
  • 仅供参考,我删除了“正确”的措辞,转而询问它是否安全。前者更多的是意见(并且可能会被关闭),后者是事实。无论哪种方式都应该得到有用的答案
  • 诚然,存储敏感数据可以解决一些可扩展性问题,但作为权衡,您会牺牲应用程序的安全性。 TBH,如果您的有效负载经过编码和加盐+您在 https 上运行,则数据受到损害的可能性不大,但是您永远不会得到任何体面的渗透测试机构的认可,并且如果您的客户必须验证这种方法,则没有这也很有可能获得批准。

标签: php jwt


【解决方案1】:

如果我粘贴到像 https://jwt.io/#debugger 这样的网站,Payload 会被解密(即使签名错误)。

第三方无法验证签名,因为他们没有密钥。 有效载荷没有被解密 - 它被解码。

理想情况下,您应该将敏感数据存储在有效负载中,因为有效负载仅经过 base64 编码且未加密。这意味着任何持有令牌的人都可以通过简单的 base64 解码来查看负载的内容。

如果您在 Web 浏览器的本地存储中有一个令牌,并且您的网站存在 XSS 漏洞,那么窃取该令牌就很容易了。攻击者拥有一个有效的 JWT(无论如何希望很快就会过期)已经够糟糕了,但如果它包含敏感数据,那么你就遇到了真正的麻烦。想象一下,由于潜在的大规模泄露,必须通知您网站上的所有用户,他们现在必须更改有关自己的各种敏感数据。

保持 JWT 轻量级。在系统中存储用户 ID、他们的角色/授权。如果您觉得必须向有效负载添加敏感数据,请尝试重新考虑您的解决方案。

【讨论】:

    【解决方案2】:
    1. 发送数据时JWT是否仅用于验证与服务器的签名?

    不,不仅有签名的 JWT (JWS - RFC 7515),还有加密的 JWT (JWE - RFC 7516)

    1. 在 Payload 中保存敏感数据是否不安全?

    当 JWT 被加密时,您可以安全地共享敏感数据(除非算法或密钥被泄露)。

    但是在您的示例中,我没有看到敏感数据,因此我想知道在您的情况下使用 JWE 是否真的很重要。 我强烈建议您向 read this blog post 了解 JWT 和会话以及为什么不应该将它们用于此目的(也请查看 that part 2)。

    如果您真的想使用 JWE,那么我写了 a PHP library,它已经能够加载和创建任何类型的 Jose (JWS/JWE),并且支持几乎所有来自 RFC 7518 的开箱即用算法。 可能存在其他库,但没有引用列表(https://jwt.io/ 仅列出 JWS 实现)。

    【讨论】:

      【解决方案3】:

      据我了解,您正在尝试拥有一个完整的 stateless 服务器,因此您甚至希望在令牌中存储敏感数据。

      但是你的服务器不可能是完全无状态的。因为对于注销功能,您必须拥有 黑名单白名单 才能使令牌无效。因此,在每个请求中,您都必须触摸 数据库。如果您没有黑名单白名单,即使用户退出,令牌仍然有效。

      因此,最好从数据库中获取敏感数据,因为您应该在每次请求时触摸您的数据库。

      【讨论】:

      • 如您所说,拥有黑名单或白名单使其不那么无国籍。所以应该避免。最好只有短暂的令牌,这样就没有“注销”动作。注销操作只是客户端删除令牌。
      • 短期到期并不能解决问题。特别是在移动应用程序中,至少您必须拥有用户希望保持登录状态的选项。(在现代应用程序中,用户永远不会退出)所以您有某种完全不包含exp 的令牌.如果用户想注销怎么办?令牌将是有效的!
      • 拥有没有exp 的令牌是一种非常糟糕的做法。拥有身份验证令牌和刷新令牌会更好(这是大多数谷歌产品的工作方式)。身份验证令牌随每个请求一起发送以授权请求,但仅在短时间内有效(例如 30 分钟)。刷新令牌用于在过期后获取新的身份验证令牌。刷新令牌应该仍然有一个过期时间,但它可以更长(比如 2 周)并且可以自行刷新。要注销,让客户端删除两个令牌。这使您可以让用户始终安全登录(只需将新令牌保密)
      • @jfadich 这是一个以正确方式构建 JWT 的好技巧。
      猜你喜欢
      • 2020-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-01
      • 1970-01-01
      • 2012-03-28
      • 1970-01-01
      • 2017-01-14
      相关资源
      最近更新 更多