首页 文章

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

提问于
浏览
85

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

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

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

基于令牌的身份验证系统只会像在每个请求中传递用户名/密码一样安全,只要它是_783534的密码 . 但是,在示例中,这意味着假设一分钟恶意用户获得服务器机密的知识,他现在可以代表任何用户生成令牌,从而不仅可以访问一个给定用户,事实上,如果获得了密码,但实际上是所有用户帐户?

这让我想到了一些问题:

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

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

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

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

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

4 回答

  • 8

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

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

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

    对于你的问题:

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

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

  • 23

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

    • 让你的JWT生命周期相对较短,并且让它's lifetime managed at the server. If you don' t,以后需要在你的JWT中需要更多信息,你必须要么支持2个版本,要么等到你的旧版JWT过期才能实现你的改变 . 如果只查看jwt中的 iat 字段,则可以在服务器上轻松管理它,并忽略 exp 字段 .

    • 考虑在JWT中包含请求的URL . 例如,如果您希望在 endpoints /my/test/path 使用JWT,在JWT中包含 'url':'/my/test/path' 之类的字段,为了确保它's only ever used at this path. If you don't,您可能会发现人们开始在其他 endpoints 使用您的JWT,即使它们未被创建对于 . 您也可以考虑包含一个md5(url),因为在JWT中有一个大网址最终会使JWT变得更大,而且它们会变得非常大 .

    如果在API中实现JWT,则每个用例都应该可以配置

    • JWT到期 . 例如,如果JWT的10个不同用例有10个 endpoints ,请确保您可以使每个 endpoints 接受在不同时间到期的JWT . 这允许您比其他 endpoints 更多地锁定某些 endpoints ,例如,如果一个 endpoints 提供的数据非常敏感 .

    • 考虑实现支持两者的JWT,而不是仅仅在一段时间后使JWT过期 .

    • N用法 - 在过期之前只能使用N次

    • 在一定时间后过期(如果你只使用一个令牌,你不希望它永生,如果不使用,是吗?)

    • 所有JWT身份验证失败都应生成“错误”响应标头,说明JWT身份验证失败的原因 . 例如“过期”,“没有使用”,“撤销”等 . 这有助于实施者了解他们的JWT失败的原因 .

    • 考虑忽略JWT的"header",因为它们泄漏信息并对黑客给予一定程度的控制 . 这主要涉及 Headers 中的 alg 字段 - 忽略这一点并假设 Headers 是您要支持的 Headers ,因为这可以避免黑客尝试使用 None 算法,从而删除签名安全检查 .

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

    • JWT 's should not be logged in log files. The contents of a JWT can be logged, but not the JWT itself. This ensures devs or others can' t从日志文件中获取JWT并对其他用户帐户执行操作 .

    • 确保您的JWT实现不允许使用"None"算法,以避免黑客在未签名的情况下创建令牌 . 通过忽略JWT的"header"可以完全避免这类错误 .

    • 强烈考虑在JWT中使用 iat (发布于)代替 exp (到期) . 为什么?由于 iat 基本上意味着创建JWT的时间,因此允许您根据创建日期在JWT到期时调整服务器 . 如果有人在未来的20年内通过 exp ,那么JWT基本上永远存在!请注意,如果JWT将来是 iat ,则会自动使其失效,但如果客户端的时间与服务器时间稍微不同步,则允许稍微摆动(例如10秒) .

    • 考虑实现一个 endpoints ,用于从json有效负载创建JWT,并强制所有实现客户端使用此 endpoints 创建其JWT . 这可确保您可以轻松地在一个位置解决JWT创建方式所需的任何安全问题 . 我们没有在我们的应用程序中直接执行此操作,现在必须慢慢地逐出JWT服务器端安全更新,因为我们的5个不同客户端需要时间来实现 . 此外,使您的创建 endpoints 接受要为JWT创建的json有效负载数组,这将减少为您的客户端进入此 endpoints 的http请求数 .

    • 如果您的JWT 's will be used at endpoints that also support use by session, ensure you don' t在您的JWT中放置满足请求所需的任何内容 . 如果在没有提供JWT的情况下确保 endpoints 使用会话,则可以轻松执行此操作 .

    • 所以JWT 's generally speaking end up containing a userId or groupId of some sort, and allow access to part of your system based on this information. Make sure you'不允许应用程序的某个区域中的用户冒充其他用户,特别是如果这样可以访问敏感数据 . 为什么?即使您的JWT生成过程只能由"internal"服务访问,开发人员或其他内部团队也可以生成JWT以访问任何用户的数据,例如一些随机客户公司的CEO . 例如,如果您的应用程序提供对客户的财务记录的访问权限,那么通过生成JWT,开发人员可以获取任何公司的财务记录!如果黑客无论如何进入你的内部网络,他们也可以这样做 .

    • 如果您要允许以任何方式缓存包含JWT的任何URL,请确保不同用户的权限包含在URL中,而不是JWT中 . 为什么?因为用户最终可能无法获取数据 . 例如,假设超级用户登录到您的应用程序,并请求以下网址: /mysite/userInfo?jwt=XXX ,并且此网址会被缓存 . 他们退出并在几分钟后,普通用户登录您的应用程序 . 他们_____3557_使用像Akamai这样的CDN,你让一些文件的寿命更长 . 这可以通过在URL中包含相关的用户信息并在服务器上验证这一点来解决,即使对于缓存的请求也是如此,例如 /mysite/userInfo?id=52&jwt=XXX

  • 44

    我不认为我是专家,但我想分享一些关于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

  • 2

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

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

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

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

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

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

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

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

相关问题