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

(从这个线程产生,因为这是一个真正的问题,而不是特定于NodeJS等)

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

但是,我正在试图了解确切地说明如何以及在多大程度上validation令牌的最佳实践,以build立真正安全的系统。 究竟应该参与“validation”令牌? 使用服务器密钥validation签名是否足够,还是应该交叉检查服务器中存储的某些数据的令牌和/或令牌载荷?

基于令牌的authentication系统只会像每个请求中传递用户名/密码一样安全,只要获取令牌的难度要高于获取用户的密码。 但是,在我看到的例子中,生成令牌所需的唯一信息是用户名和服务器端的秘密。 这是否意味着假设一个恶意用户获得了有关服务器机密的知识,他现在可以代表任何用户生成令牌,从而不仅可以访问给定用户,而且密码是获得,但实际上所有的用户帐户?

这带来了我的问题:

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

  • 在某些情况下,我已经看到了令牌和服务器会话的组合使用,在通过/ login端点成功login后,会话被build立。 API请求validation令牌,并将令牌中发现的解码数据与会话中存储的一些数据进行比较。 但是,使用会话意味着使用cookie,从某种意义上讲,它违背了使用基于令牌的方法的目的。 这也可能会导致某些客户的问题。

  • 可以想象,服务器将所有令牌当前正在使用的内存caching或类似内容保留下来,以确保即使服务器秘密受到攻击,攻击者可以生成“有效”令牌,但只能通过/ login端点生成的确切令牌会被接受。 这是合理的还是多余的/矫枉过正的?

2)如果JWT签名validation是validation令牌的唯一手段,意味着服务器秘密的完整性是突破点,那么应该如何pipe理服务器机密? 从环境variables中读取并在每个已部署的堆栈上创build(随机化?)一次? 重新定制或定期轮换(如果是的话,如何处理在循环之前创build的现有有效令牌,但需要在循环后进行validation,如果服务器在任何给定的时间保持当前秘密和先前的秘密, ? 别的东西?

当涉及到服务器机密被泄密的风险时,也许我只是过于偏执狂,这当然是一个更加普遍的问题,需要在所有密码的情况下解决。

我一直在为我的应用程序玩令牌。 虽然我不是专家,但我可以分享一些我在这个问题上的经验和想法。

智威汤逊的本质就是诚信。 它为您的服务器提供了一种机制,validation提供给它的令牌是否是真实的,并且是由服务器提供的。 通过你的秘密产生的签名是为此提供的。 所以,是的,如果你的秘密以某种方式被泄露,那么这个人可以产生你的服务器认为是它自己的令牌。 简单地说,由于签名validation,基于令牌的系统仍然比您的用户名/密码系统更安全。 而在这种情况下,如果有人有你的秘密,你的系统还有其他安全问题要处理,而不是有人伪造令牌(即使这样,只是改变秘密,以确保任何旧的秘密令牌现在是无效的)。

至于有效载荷,签名只会告诉你提供给你的令牌和你的服务器发出的令牌完全一样。 validation有效载荷内容是否有效或适合您的应用程序显然取决于您。

对于你的问题:

1.)在我有限的经验中,用第二个系统来validation你的标记绝对是更好的select。 简单地validation签名就意味着令牌是用你的秘密生成的。 将任何创build的令牌存储在某种types的数据库(redis,memcache / sql / mongo或其他存储)中是确保您只接受服务器创build的令牌的绝佳方式。 在这种情况下,即使你的秘密被泄露,也不会有太大的问题,因为任何生成的令牌都不会有效。 这是我正在使用我的系统的方法 – 所有生成的令牌都存储在一个数据库(redis)和每个请求,我确认该令牌是在我的数据库之前,我接受它。 这种方法令牌可以由于任何原因被撤销,比如以某种方式被释放到野外的令牌,用户注销,密码改变,秘密改变等等。

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

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

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

    答:我处理它的方式:我将生成的散列存储到会话存储器中,并显示expiricy时间。 要validation令牌,它需要由服务器发出。

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

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

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

c:使用会话ID的cookie不会有助于validation您的令牌。 如果有人想劫持一个lambda用户的会话,他只需要使用一个嗅探器(例如:wireshark)。 这个黑客同时拥有两个信息。

  • 2:每个秘密都是一样的。 总有一种方法可以知道它。

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

但是,我正在试图了解确切地说明如何以及在多大程度上validation令牌的最佳实践,以build立真正安全的系统。

如果你想要最好的安全可能,你不应该盲目地遵循最佳实践。 最好的方法就是理解你在做什么(我认为当我看到你的问题时是可以的),然后评估你所需要的安全性。 如果摩萨德想要访问您的机密数据,他们总是会find一个办法。 (我喜欢这个博客文章: https : //www.schneier.com/blog/archives/2015/08/mickens_on_secu.html )

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

  • 保持JWT的生命周期相对较短,并在服务器上进行生命周期pipe理。 如果您没有,并且以后需要在JWT中需要更多信息,那么您必须支持2个版本,或者等到您的旧JWT过期后再执行更改。

  • 考虑在你的JWT中包含请求的URL。 例如,如果您希望在端点/my/test/path使用JWT,请在您的JWT中包含像'url':'/my/test/path'这样的字段,以确保它仅在此path中使用。 如果你不这样做,你可能会发现人们开始在其他端点使用你的JWT,甚至是那些没有被创build的JWT。

  • 如果JWT正在API中实现,JWT到期应该可以由每个用例configuration。 例如,如果针对JWT的10个不同用例有10个端点,请确保您可以使每个端点接受在不同时间到期的JWT。 这样,如果某个端点提供的数据非常敏感,则可以locking某些端点。

  • 不要在一段时间之后简单地过期JWT,而是考虑实施支持JWT的:

    • N用途 – 只能在到期前使​​用N次
    • 在一定的时间后过期(如果你有一个只使用一个令牌,你不希望它永远活着,如果不使用,是吗?)
  • 所有JWT身份validation失败应该生成一个“错误”响应标头,说明JWT身份validation失败的原因。 例如“过期”,“不使用”,“撤销”等。这有助于实施人员知道为什么他们的JWT失败。

  • 考虑忽略你的智威汤逊的“标题”,因为他们泄露信息,并给黑客一个控制措施。

  • 智威汤逊应包括一个标识符,详细说明哪个应用程序生成的令牌。 例如,如果您的JWT由两个不同的客户端mychat和myclassifiedsapp创build,那么每个客户端都应该在JWT的“iss”字段中包含项目名称或类似信息,例如“iss”:“mychat”

  • JWT不应该login日志文件。 JWT的内容可以被logging,但不是JWT本身。 这样可以确保开发者或其他人不能从日志文件中抓取JWT,而是将其他用户帐户操作。
  • 确保您的JWT实施不允许使用“无”algorithm,以避免黑客创build令牌而无需签名。 这类错误完全可以通过忽略JWT的“标题”来避免。
  • 在您的JWTs中强烈考虑使用iat (发放地址)而不是exp (到期)。 为什么? 由于iat基本上意味着JWT创build的时间,这允许您根据创builddate在JWT到期时在服务器上进行调整。 如果有人通过未来20年的exp ,智威汤逊基本上永远活着! 请注意,如果将来他们的iat将会自动失效,但是如果客户端的时间与服务器的时间稍微不同步,则允许有一点摆动空间(例如10秒)。
  • 考虑实现一个从json载荷创buildJWT的端点,并强制所有的实现客户端使用这个端点创build他们的JWT。 这可以确保您可以轻松地解决任何需要使用JWT创build的安全问题。 我们没有在我们的应用程序中直接执行此操作,现在必须慢慢地将JWT服务器端安全更新缓冲下来,因为我们的5个不同的客户端需要时间实施。 另外,让你的创build端点接受一个JSON有效载荷数组供JWT创build,这将减less进入这个端点的客户端的http请求数量。

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

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

不,因为与令牌机密的妥协无关的原因。 每当用户通过用户名和密码login时,授权服务器应该存储生成的令牌或者生成的令牌的元数据。 将此元数据视为授权logging。 给定的用户和应用程序对在任何给定时间应该只有一个有效的令牌或授权。 有用的元数据是与访问令牌,应用程序ID以及访问令牌被发布的时间(其允许撤销现有的访问令牌并发布新的访问令牌)相关联的用户ID。 在每个API请求上,validation令牌包含适当的元数据。 您需要坚持有关每个访问令牌何时发布的信息,以便用户可以撤销现有的访问令牌(如果其帐户凭据受到威胁),然后再次login并开始使用新的访问令牌。 这将更新数据库与访问令牌发出的时间(创build授权时间)。 在每个API请求上,检查访问令牌的颁发时间是否在创build授权时间之后。

其他安全措施包括不loggingJWT并需要像SHA256这样的安全签名algorithm。

2)如果JWT签名validation是validation令牌的唯一手段,意味着服务器秘密的完整性是突破点,那么应该如何pipe理服务器机密?

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

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

然而,一个系统,以减轻损害的服务器机密,并避免在源代码中存储秘密涉及使用像https://zookeeper.apache.org协调服务令牌秘密轮换。; 使用cron作业每隔几个小时左右生成一个应用程序密码(无论您的访问令牌是否有效),并将更新后的密码推送到Zookeeper。 在每个需要知道令牌密钥的应用程序服务器中,configuration每当ZK节点值发生更改时更新的ZK客户端。 存储一个主密钥和一个次密钥,每次更改密钥密码时,将新密钥设置为主密钥,将旧密码设置为次密钥。 这样,现有的有效令牌将仍然有效,因为它们将通过二级密码进行validation。 当二级密码被旧的主密码replace时,所有与二级密码一起发出的访问令牌将会过期。