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

时间:2015-05-29 07:06:18

标签: security authentication token jwt secret-key

(由this thread产生,因为这实际上是一个问题,而不是NodeJS特有的等等)

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

但是,我正在努力了解有关如何以及在何种程度上验证令牌的最佳实践,以构建真正安全的系统。究竟什么应该参与“验证”令牌?是否可以使用server-secret验证签名,还是我还应该针对存储在服务器中的某些数据交叉检查令牌和/或令牌有效负载?

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

这让我想到了问题:

1)JWT令牌验证是否应限于验证令牌本身的签名,单独依赖服务器机密的完整性,还是附带单独的验证机制?

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

  • 可以想象服务器保留当前在memcache或类似文件中使用的所有令牌,以确保即使服务器机密被泄露,以便攻击者可以产生“有效”令牌,只有确切的令牌是通过/ login端点生成的将被接受。这是合理的还是仅仅是多余/过度的?

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

当谈到服务器机密被泄露的风险时,我可能只是过于偏执,这当然是一个需要在所有加密情况下解决的更普遍的问题......

5 个答案:

答案 0 :(得分:52)

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

JWT的观点基本上是完整性。它为您的服务器提供了一种机制,以验证提供给它的令牌是否为真品并由您的服务器提供。通过您的秘密生成的签名就是为此提供的。所以,是的,如果您的秘密以某种方式泄露,该个人可以生成您的服务器认为是自己的令牌。由于签名验证,基于令牌的系统仍然比您的用户名/密码系统更安全。在这种情况下,如果有人有你的秘密,你的系统还有其他安全问题要处理,而不是制造假代币的人(即便如此,只需更改密码就可以确保使用旧秘密制作的任何代币现在都无效)。 / p>

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

对于你的问题:

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

2。)这是我没有太多经验的东西,而且我还在积极研究,因为我不是安全专业人士。如果您找到任何资源,请随时在这里发布!目前,我只是使用从磁盘加载的私钥,但显然这远不是​​最好或最安全的解决方案。

答案 1 :(得分:35)

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

  • 让您的JWT生命周期相对较短,并在服务器上进行终身管理。如果您不需要,以后需要在JWT中需要更多信息,那么您必须支持2个版本,或者等到旧版JWT过期后才能实施更改。如果您只查看jwt中的iat字段,则可以在服务器上轻松管理它,并忽略exp字段。

  • 考虑在JWT中包含请求的网址。例如,如果您希望在端点/my/test/path使用JWT,请在JWT中包含'url':'/my/test/path'之类的字段,以确保它仅在此路径中使用过。如果你不这样做,你可能会发现人们开始在其他终端使用你的JWT,即使他们没有创建它们。您也可以考虑包含一个md5(url),因为在JWT中有一个大的URL会最终使JWT变得更大,并且它们会变得非常大。

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

  • 考虑实施支持两者的JWT,而不是简单地在一段时间后使JWT到期:

    • N用法 - 只能在到期前使​​用N次
    • 在一定时间后过期(如果您只使用一个令牌,如果不使用,您不希望它永远存在,是吗?)
  • 所有JWT身份验证失败都应生成"错误"响应标头,说明JWT身份验证失败的原因。例如"已过期","没有剩余使用","已撤销"等。这有助于实施者了解他们的JWT失败的原因。

  • 考虑忽略"标题"您的JWT泄露信息并对黑客进行一定程度的控制。这主要与标题中的alg字段有关 - 忽略这一点并假设标题是您要支持的标题,因为这可以避免黑客尝试使用None算法,这会删除签名安全性检查。

  • JWT应该包含一个标识符,详细说明哪个应用程序生成了令牌。例如,如果您的JWT由2个不同的客户端,mychat和myclassifiedsapp创建,那么每个客户端都应该包含它的项目名称或类似的东西,并且#34; iss" JWT中的字段,例如" 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中。为什么?因为用户最终可能无法获取数据,所以他们不应该这样做。例如,假设超级用户登录到您的应用,并请求以下网址:/mysite/userInfo?jwt=XXX,并且此网址会被缓存。他们退出并在几分钟后,普通用户登录您的应用程序。他们将获得缓存的内容 - 包含超级用户的信息!这往往在客户端上发生的情况较少,在服务器上发生的情况更多,特别是在您使用像Akamai这样的CDN的情况下,并且您让某些文件的存活时间更长。这可以通过在URL中包含相关的用户信息并在服务器上验证这一点来解决,即使对于缓存的请求也是如此,例如/mysite/userInfo?id=52&jwt=XXX
  • 如果您的jwt旨在像会话cookie一样使用,并且只能在创建jwt的同一台机器上工作,您应该考虑在您的jwt中添加jti字段。这基本上是一个CSRF令牌,可确保您的JWT无法从一个用户的浏览器传递给其他人。

答案 2 :(得分:9)

我认为我不是专家,但我想分享一些关于Jwt的内容。

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

    a。:我处理它的方式:我使用expiricy时间将生成的哈希存储到会话存储中。要验证令牌,它需要由服务器发出。

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

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

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

c。:使用带有会话ID的cookie对验证令牌没有用。如果有人想劫持lambda用户的会话,他只需要使用嗅探器(例如:wireshark)。这个黑客可以同时获得这两种信息。

  • 2:每个秘密都是一样的。总有办法知道它。

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

  

但是,我正在尝试了解具体方法的最佳做法   并且应该在多大程度上验证令牌,以真正做到   安全系统。

如果您希望获得最佳安全性,则不应盲目遵循最佳做法。最好的方法是了解你正在做什么(我认为当我看到你的问题时它没问题),然后评估你需要的安全性。如果摩萨德想要访问您的机密数据,他们总会找到一种方法。 (我喜欢这篇博文:https://www.schneier.com/blog/archives/2015/08/mickens_on_secu.html

答案 3 :(得分:3)

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

1)JWT令牌验证是否应限于验证令牌本身的签名,单独依赖服务器机密的完整性,还是附带单独的验证机制?

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

其他安全措施包括不记录JWT并需要像SHA256这样的安全签名算法。

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

服务器机密的泄密将允许攻击者为任何用户发出访问令牌,并且在步骤1中存储访问令牌数据不一定会阻止服务器接受这些访问令牌。例如,假设已向用户发出访问令牌,然后攻击者为该用户生成访问令牌。访问令牌的授权时间有效。

就像Akshay Dhalwala所说,如果您的服务器端机密被泄露,那么您需要处理更大的问题,因为这意味着攻击者已经破坏了您的内部网络,源代码存储库或两者兼而有之。

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

答案 4 :(得分:0)

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