服务器端处理JWT令牌的最佳实践


111

(从此线程产生,因为这实际上是一个问题,并不特定于NodeJS等)

我正在通过身份验证实现REST API服务器,并且已经成功实现了JWT令牌处理,以便用户可以使用用户名/密码通过/ login端点登录,然后从服务器机密生成JWT令牌并将其返回给客户。然后,在每个经过身份验证的API请求中,将令牌从客户端传递到服务器,然后使用服务器机密来验证令牌。

但是,我试图了解关于如何以及在多大程度上验证令牌的最佳实践,以构成一个真正安全的系统。“验证”令牌应该包含什么内容?可以使用服务器秘密来验证签名是否足够,还是我还应该对照服务器中存储的某些数据来交叉检查令牌和/或令牌有效载荷?

基于令牌的身份验证系统仅与在每个请求中传递用户名/密码一样安全,只要获取令牌比获取用户密码同等或更困难。但是,在我看到的示例中,生成令牌所需的唯一信息是用户名和服务器端机密。这不是说,假设一分钟内恶意用户获得了服务器机密知识,他现在就可以代表任何用户产生令牌,从而不仅可以访问一个给定的用户,而且如果密码为获得,但实际上是所有用户帐户?

这使我想到了以下问题:

1)JWT令牌验证是否应仅限于验证令牌本身的签名,仅依靠服务器机密的完整性或通过单独的验证机制进行验证?

  • 在某些情况下,我已经看到了令牌和服务器会话的组合使用,其中在通过/ login端点成功登录后会建立一个会话。API请求会验证令牌,还将令牌中找到的解码数据与会话中存储的某些数据进行比较。但是,使用会话意味着使用cookie,从某种意义上说,它违反了使用基于令牌的方法的目的。这也可能给某些客户带来麻烦。

  • 可以想象服务器将当前所有令牌都保留在内存缓存或类似的内存中,以确保即使服务器机密受到威胁,攻击者也可以生成“有效”令牌,只有通过/ login端点生成的确切令牌将被接受。这是合理的还是仅仅是多余/过度的?

2)如果JWT签名验证是验证令牌的唯一方法,这意味着服务器密钥的完整性是切入点,那么应该如何管理服务器密钥?从环境变量读取并为每个部署的堆栈创建一次(随机化?)?定期更新或轮换(如果这样,如何处理在轮换之前创建但在轮换之后需要验证的现有有效令牌,如果服务器在任何给定时间保留当前和先前的机密就足够了) ?还有吗

当涉及到服务器机密受到威胁的风险时,我可能只是过于偏执,这当然是一个更普遍的问题,需要在所有加密情况下解决……


1
有很大的问题。回复:问题2。我在服务器端保存的任何秘密密钥都存在相同的问题。如果要执行任何形式的哈希匹配或非对称解密,无论是对jwt进行签名还是对存储在数据库中的抄送信息进行解密,您都必须拥有服务器上可通过代码访问的秘密密钥。那你到底在哪里呢?这是我找到的最好的答案:pcinetwork.org/forum/index.php?threads/… -可能和jwt密钥一样安全。
jbd

jwt令牌中的秘密密钥是什么?我认为jwt令牌本身是个秘密。或秘密密钥可能是RSAPrivateKey privateKey??
kittu 2016年

3
有人问过这个,但是也许有人会觉得有用。就我而言,每个用户都有一个“秘密密钥”。因此,每次用户登录时,我都会生成该秘密并将其与用户记录一起存储在数据库中。我使用该秘密验证令牌。注销后,我清除该值。这会自动使之前创建的其他令牌无效(这就是我所需要的)。
尼尔森·罗德里格斯

Answers:


52

我也一直在为应用程序使用令牌。虽然我绝不是专家,但我可以分享一些有关此事的经验和想法。

JWT的本质是完整性。它为服务器提供了一种机制,以验证提供给它的令牌是真实的并由服务器提供。通过您的机密生成的签名就是为此提供了条件。因此,是的,如果您的机密以某种方式泄露,那么该人可以生成您的服务器认为是自己的令牌。仅由于签名验证,基于令牌的系统仍将比您的用户名/密码系统更安全。在这种情况下,如果有人拥有您的机密,那么您的系统将面临其他安全问题,而不是制作伪造令牌的人(即使那样,仅更改机密也可以确保使用旧机密制作的任何令牌现在都无效)。

至于有效负载,签名只会告诉您提供给您的令牌与服务器发送令牌时的令牌完全相同。验证有效载荷内容对您的应用程序有效还是合适显然取决于您。

对于您的问题:

1.)以我有限的经验,最好使用第二个系统来验证您的令牌。简单地验证签名就意味着令牌是使用您的秘密生成的。将所有已创建的令牌存储在某种类型的数据库(redis,memcache / sql / mongo或某些其他存储)中是一种绝妙的方式,可确保您仅接受服务器已创建的令牌。在这种情况下,即使您的机密被泄露,也不会太重要,因为任何生成的令牌都将是无效的。这是我在系统上采用的方法-所有生成的令牌都存储在数据库(redis)中,并且在接受每个请求时,在接受令牌之前,我先验证令牌是否在数据库中。这样,令牌可以出于任何原因而被吊销,例如以某种方式释放到野外的令牌,用户注销,密码更改,机密更改等。

2.)这是我经验不足的事情,而且由于我不是安全专业人员,所以我仍在积极研究中。如果您找到任何资源,请随时在此处发布!目前,我只是使用从磁盘加载的私钥,但是显然这与最佳或最安全的解决方案相去甚远。


5
对于这里的第二点是一个很好的答案:security.stackexchange.com/questions/87130/...
Bossliaw

1
由于令牌在标头中可用,如果令牌被盗并且恶意尝试使用该令牌登录(知道用户的电子邮件地址)怎么办?
kittu '16

22
如果您存储每个JWT,那么JWT就没有任何好处,因此您最好坚持使用随机会话ID。
ColinM '17

46

在您的应用程序中实现JWT时,需要考虑以下事项:

  • 使您的JWT生存期相对较短,并在服务器上管理其生存期。如果不这样做,以后又需要在JWT中要求更多信息,则必须支持2个版本,或者等到较旧的JWT过期后才能实施更改。如果仅查看iatjwt中的exp字段,而忽略该字段,则可以在服务器上轻松管理它。

  • 考虑在JWT中包含请求的网址。例如,如果要在端点使用JWT,请在JWT中/my/test/path包含一个类似字段'url':'/my/test/path',以确保仅在此路径上使用它。如果不这样做,您可能会发现人们开始在其他端点上使用您的JWT,即使不是为它们创建的。您还可以考虑改为包含md5(url),因为JWT中的URL较大将最终使JWT变得更大,并且它们可能变得很大。

  • 如果要在API中实现JWT,则JWT到期时间可以根据每个用例进行配置。例如,如果您有10个端点用于JWT的10个不同用例,请确保可以使每个端点接受在不同时间到期的JWT。例如,如果一个端点提供的数据非常敏感,则可以使某些端点比其他端点更多地锁定。

  • 可以考虑实现同时支持这两种方式的JWT,而不是在一定时间后简单地使JWT过期:

    • N次使用-在过期之前只能使用N次,并且
    • 在一定时间后过期(如果您有一个只能使用的令牌,您不希望它在没有使用的情况下永远存在,对吗?)
  • 所有JWT身份验证失败都应生成一个“错误”响应标头,其中指出JWT身份验证失败的原因。例如,“过期”,“没有剩余使用量”,“已撤销”等。这可以帮助实现者了解其JWT失败的原因。

  • 当JWT泄漏信息并给予黑客一定程度的控制时,请考虑忽略它们的“标头”。这主要涉及alg标头中的字段-忽略此内容,仅假设标头是您要支持的标头即可,因为这可以避免黑客试图使用该None算法,从而消除了签名安全性检查。

  • JWT应该包含一个标识符,详细说明哪个应用程序生成了令牌。例如,如果您的JWT是由2个不同的客户端mychat和myclassifiedsapp创建的,则每个客户端应在JWT的“ iss”字段中包含其项目名称或类似名称,例如“ iss”:“ mychat”

  • JWT不应记录在日志文件中。可以记录JWT的内容,但不能记录JWT本身。这样可以确保开发人员或其他人员无法从日志文件中获取JWT并将其处理给其他用户帐户。
  • 确保您的JWT实现不允许使用“无”算法,以避免黑客创建令牌而不对其进行签名。可以通过忽略JWT的“标题”来完全避免此类错误。
  • 强烈考虑在JWT中使用iat(发出于)而不是exp(到期)。为什么?由于iat基本上是指JWT的创建时间,因此,您可以根据创建日期在JWT到期时在服务器上进行调整。如果有人在exp未来的20年内过世,那么JWT基本上可以永生!请注意,如果JWT iat将来会自动过期,但是会留出一些摆动空间(例如10秒),以防客户端的时间与服务器时间略有不同步。
  • 考虑实现一个用于从json有效负载创建JWT的终结点,并强制所有实现的客户端使用该终结点来创建其JWT。这样可以确保您可以轻松地解决如何在一个地方创建JWT的任何安全问题。我们没有在应用程序中立即执行此操作,现在必须缓慢地滴灌JWT服务器端安全更新,因为我们的5个不同的客户端需要时间来实施。同样,使您的创建端点接受JWT创建的json有效载荷数组,这将减少客户端进入此端点的http请求的数量。
  • 如果您的JWT将在也支持按会话使用的端点上使用,请确保没有在JWT中放入满足请求所需的任何内容。如果在没有提供JWT的情况下确保端点可以与会话一起使用,则可以轻松地做到这一点。
  • 因此,一般而言,JWT最终包含某种形式的userId或groupId,并允许基于此信息访问系统的一部分。确保您不允许应用程序某个区域中的用户冒充其他用户,特别是如果这提供了对敏感数据的访问权限时。为什么?好的,即使“内部”服务只能访问您的JWT生成过程,开发人员或其他内部团队也可以生成JWT来访问任何用户的数据,例如某个随机客户公司的首席执行官。例如,如果您的应用程序为客户提供了对财务记录的访问权限,那么通过生成JWT,开发人员可以完全获取任何公司的财务记录!而且,如果黑客无论如何进入您的内部网络,他们都可以这样做。
  • 如果要允许以任何方式缓存任何包含JWT的URL,请确保该URL中包含不同用户的权限,而不是JWT。为什么?由于用户最终可能会获取数据,因此他们不应该这样做。例如,假设一个超级用户登录到您的应用程序,并请求以下URL:,/mysite/userInfo?jwt=XXX并且该URL被缓存。他们注销后,几分钟后,普通用户登录到您的应用程序。他们将获得缓存的内容-有关超级用户的信息!这种情况在客户端上的发生较少,而在服务器上的发生则更多,尤其是在使用Akamai之类的CDN并使某些文件的寿命更长的情况下。可以通过以下方式解决此问题:在url中包含相关的用户信息,并在服务器上进行验证,例如对于缓存的请求,例如/mysite/userInfo?id=52&jwt=XXX
  • 如果您的jwt打算像会话cookie一样使用,并且只能在为其创建jwt的同一台机器上工作,则应考虑向jwt 添加一个jti字段。这基本上是CSRF令牌,可确保您的JWT无法从一个用户的浏览器传递到另一个用户的浏览器。

1
您所说created_by的,在JWT中已经有一个索偿要求,它被称为iss(签发人)。
Fred

是的,很好-我将对此进行更新...谢谢!
布拉德·帕克斯

8

我不认为我是专家,但我想分享一些有关Jwt的信息。

  • 1:正如Akshay所说,最好有第二个系统来验证您的令牌。

    答:我的处理方式:我将产生的哈希值随时间存储到会话存储中。要验证令牌,它需要由服务器发行。

    b .:至少必须检查一件事,即所使用的签名方法。例如:

    header :
    {
      "alg": "none",
      "typ": "JWT"
    }
    

一些验证JWT的库将不检查哈希就接受这一代码。这意味着,在不知道您的盐用于签名令牌的情况下,黑客可以授予自己一些权利。始终确保这不会发生。 https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/

c .:将cookie与会话ID一起使用对验证令牌无效。如果有人想劫持lambda用户的会话,则只需要使用嗅探器即可(例如:wireshark)。该黑客将同时拥有这两个信息。

  • 2:每个秘密都是一样的。总有一种了解的方法。

我的处理方式与1.a点有关。:我有一个与随机变量混合的秘密。每个令牌的秘密都是唯一的。

但是,我试图了解关于如何以及在多大程度上验证令牌的最佳实践,以构成一个真正安全的系统。

如果希望获得最佳安全性,则不应盲目遵循最佳实践。最好的方法是了解您在做什么(当我看到您的问题时,我认为可以),然后评估所需的安全性。而且,如果Mossad希望访问您的机密数据,他们将始终找到方法。(我喜欢这篇博客文章:https : //www.schneier.com/blog/archives/2015/08/mickens_on_secu.html


为每个令牌都拥有一个唯一秘密的好时机,但是您如何每次创建一个唯一秘密呢?我正在使用nimbus jwt库
kittu,2016年

1
可能使用您用户的哈希密码。
momokjaaaaa

1
“如果您做事的方式与其他人不同,那么人们将很难找到通过您的安全性的方法。” 对我来说,这听起来像是“通过模糊实现安全”。之所以称其为最佳实践,是因为它们以实际的方式减轻了最常见的风险。
Mnebuerquo

@Mnebuerquo,我完全同意你的看法,写那个不值得信任的人;-)
Deblaton Jean-Philippe

1
他是正确的,尽管不应盲目地遵循最佳做法。最好理解为什么最佳实践被认为是最佳的。在每个安全设计决策中,都必须在安全性和可用性之间进行权衡。了解原因意味着您可以明智地做出这些决定。(不过,请遵循最佳做法,因为您的用户不
会这样做

3

这里有很多好的答案。我将整合一些我认为最相关的答案,并添加更多建议。

1)JWT令牌验证是否应仅限于验证令牌本身的签名,仅依靠服务器机密的完整性或通过单独的验证机制进行验证?

不可以,因为与令牌机密泄露无关的原因。每次用户通过用户名和密码登录时,授权服务器都应存储生成的令牌或有关生成的令牌的元数据。将此元数据视为授权记录。给定的用户和应用程序对在任何给定时间仅应具有一个有效令牌或授权。有用的元数据是与访问令牌关联的用户ID,应用程序ID,以及发布访问令牌的时间(这允许吊销现有访问令牌和发布新的访问令牌)。在每个API请求中,验证令牌是否包含正确的元数据。您需要保留有关每个访问令牌发布时间的信息,这样,如果用户的帐户凭据遭到破坏,用户便可以撤消现有的访问令牌,然后再次登录并开始使用新的访问令牌。这将使用发出访问令牌的时间(创建的授权时间)更新数据库。在每个API请求中,检查访问令牌的发布时间是否在创建授权时间之后。

其他安全措施包括不记录JWT并需要安全签名算法(例如SHA256)。

2)如果JWT签名验证是验证令牌的唯一方法,这意味着服务器密钥的完整性是切入点,那么应该如何管理服务器密钥?

服务器机密的泄露将使攻击者可以为任何用户发布访问令牌,并且在步骤1中存储访问令牌数据不一定会阻止服务器接受那些访问令牌。例如,假设某个用户已被授予访问令牌,然后攻击者随后为该用户生成访问令牌。访问令牌的授权时间将是有效的。

就像Akshay Dhalwala所说的那样,如果您的服务器端机密受到了威胁,那么您将面临更大的问题,因为这意味着攻击者已经破坏了您的内部网络,您的源代码存储库或这两者。

但是,为减轻受到破坏的服务器机密的损害并避免将机密存储在源代码中的系统,需要使用https://zookeeper.apache.org等协调服务来进行令牌机密轮换。。使用cron作业每隔几个小时左右(无论您的访问令牌对它有效的时间)生成一个应用程序密码,然后将更新后的密码推送到Zookeeper。在每个需要了解令牌机密的应用服务器中,配置ZK客户端,该客户端在ZK节点值更改时会更新。存储主密钥和辅助密钥,并且每次更改令牌密钥时,都将新令牌密钥设置为主密钥,将旧令牌密钥设置为辅助密钥。这样,现有有效令牌将仍然有效,因为它们将针对次要秘密进行验证。到第二个机密被旧的第一个机密替换时,由第二个机密发出的所有访问令牌都将过期。


0

IETF在oAuth工作组中正在进行RFC,请参阅:https ://tools.ietf.org/id/draft-ietf-oauth-jwt-bcp-05.html

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.