2015-05-29 49 views
66

(从this thread催生因为这是真的具体到等的NodeJS自己的,而不是一个问题)最佳实践令牌

我实现与验证的REST API服务器,我已经成功实现了JWT令牌处理,以便用户可以通过带有用户名/密码的/ login端点进行登录,然后根据服务器密钥生成JWT令牌并将其返回给客户端。然后在每个经过验证的API请求中将令牌从客户端传递到服务器,服务器密钥用于验证令牌。

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

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

这使我想到的问题:

1)如果JWT令牌验证由一个单独的验证机制仅限于核实令牌本身的签名,单独依靠服务器秘密的完整性,或伴有?

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

  • 可以想象服务器将所有令牌当前在memcache或类似目录中使用,以确保即使服务器机密受到攻击,以至于攻击者可能产生“有效”令牌,但只会产生确切的令牌通过/登录端点将被接受。这是合理的还是多余的/矫枉过正的?

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

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

+1

有很大的问题。回复:问题2我有任何密钥保持服务器端相同的问题。如果您正在执行任何类型的散列匹配或非对称解密,无论是签名jwt还是解密存储在数据库中的cc信息,您都必须通过服务器上的代码访问密钥。那么你到底在哪里?以下是我找到的最佳答案:https:// pcinetwork。组织/论坛/的index.php?线程/ PCI-DSS-3-0-3-5-2店秘密和私有密钥,用来对加密 - 解密 - 持卡人数据中,单或更多的619//可能安全,因为它也得到一个jwt密钥。 – jbd

+0

什么是jwt token中的密钥?我认为jwt标志本身就是一个秘密。或者密钥可能是'RSAPrivateKey privateKey'? – kittu

+2

刚才有人问到,但也许有人会觉得它有用。就我而言,每个用户都有一个“密钥”。所以每次用户登录时,我都会生成该密码并将用户记录存储在数据库中。我使用该秘密验证令牌。注销后,我可以清除该值。这会自动使其他创建的令牌失效(这就是我所需要的)。 –

回答

35

我一直在为我的应用程序玩代币。尽管我不是专家,但我可以分享我在这件事上的一些经验和想法。

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

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

对于您的问题:

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

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

+5

对于第二点这里是一个很好的答案: http://security.stackexchange.com/questions/87130/json-web-tokens-how-to-securely-store-the-key – Bossliaw

+0

作为令牌可用头部,如果令牌被盗并且恶意尝试用该令牌登录(知道用户的电子邮件地址)会怎么样? – Satyadev

+1

如果您存储了每个JWT,那么对JWT没有任何好处,您可以坚持使用随机会话ID。 – ColinM

8

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

  • 1:作为阿克沙伊说,最好是有一个第二个系统,以验证您的令牌。

    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

+0

对于每个令牌都有一个独特的秘密的好处,但是如何每次创建一个独特的秘密?我正在使用nimbus jwt库 – Satyadev

+1

可能会使用您的用户的散列密码。 – momokjaaaaa

+1

“如果你的做法与其他人做的不一样,那么人们通过你的安全来找到一个方法会更困难。”这听起来像安全通过默默无闻给我。最佳实践被称为是因为它们以实用的方式缓解了最常见的风险。 – Mnebuerquo

13

这里有一些事情落实JWT在你的应用程序时需要考虑:

  • 保持你的智威汤逊寿命比较短,而且有它的生命周期在服务器管理。如果您没有,并且以后需要在JWT中需要更多信息,则必须支持2个版本,或者等到您的旧JWT过期后再执行更改。如果只查看jwt中的iat字段,并忽略exp字段,则可以在服务器上轻松管理它。

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

  • 如果JWT正在API中实施,JWT失效应该可以由每个用例进行配置。例如,如果针对JWT的10个不同用例包含10个端点,请确保您可以让每个端点接受在不同时间到期的JWT。这允许您比其他端点锁定更多端点,例如,一个端点提供的数据非常敏感。

  • 而是在一定时间后简单地到期JWTs的,考虑实施支持JWTs:

    • ñ用途 - 它们到期之前只能用N次
    • 一定的时间后失效(如果您有一个仅使用令牌,你不希望它永远活不使用时,你呢?)
  • 所有JWT验证失败将产生一个“错误”响应报头,指出为什么JWT真实ation失败。例如“过期”,“不使用剩余”,“撤销”等。这有助于实施人员知道他们的智威汤逊为何失败。

  • 考虑忽略JWT的“标题”,因为他们泄漏信息并给黑客控制措施。这主要是关于头部中的alg字段 - 忽略这一点,只是假设头部是你想要支持的,因为这样可以避免黑客试图使用None算法,该算法可以消除签名安全检查。

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

  • 智威汤逊不应该登录日志文件。 JWT的内容可以被记录,但不是JWT本身。这可以确保开发人员或其他人无法从日志文件中获取JWT,并可以将其他用户帐户操作。
  • 确保您的JWT实施不允许使用“无”算法,以避免黑客在未签署令牌的情况下创建令牌。这类错误可以通过忽略JWT的“标题”完全避免。
  • 在您的JWT中强烈考虑使用iat(发放时间)而不是exp(到期)。为什么?由于iat基本上表示JWT何时创建,因此您可以根据创建日期在JWT过期时调整服务器。如果有人通过未来20年的exp,智威汤逊基本上永远活着!请注意,如果将来他们的iat将自动失效,但允许有一点摆动空间(例如10秒),以防客户端的时间稍微与服务器时间不同步。
  • 考虑实现从json有效内容创建JWT的端点,并强制所有实现的客户端使用此端点来创建其JWT。这可以确保您可以轻松地解决任何想要在一个地方创建JWT的安全问题。我们并没有在我们的应用程序中直接执行此操作,而现在不得不慢慢地将JWT服务器端安全更新慢慢推出,因为我们的5个不同客户端需要时间实施。此外,使您的创建端点接受JWT有效负载的数组以供JWT创建,这将减少为您的客户端进入此端点的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
+1

您称之为'created_by',在JWT中已经有一个声明,它被称为'iss'(issuer)。 – Fred

+0

是的好点 - 我会更新与...谢谢! –

1

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

1)如果JWT令牌验证限于验证令牌本身的签名,单独依靠服务器秘密的完整性,或伴有一个单独的验证机制?

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

其他安全措施包括不记录JWTs并要求安全签名算法像SHA256。

2)如果JWT签名验证是验证令牌,这意味着服务器秘密的完整性的唯一手段是突破点,应该如何服务器秘密进行管理?

服务器秘密的折衷办法允许攻击者发出访问令牌的任何用户,并在步骤1中存储的访问令牌的数据将不一定防止服务器接受的那些接入令牌。例如,假设用户已经获得访问令牌,然后攻击者为该用户生成访问令牌。访问令牌的授权时间将是有效的。

就像Akshay Dhalwala说的,如果你的服务器端的机密被攻破,那么你就有更大的问题需要处理,因为这意味着攻击者已经损害了你的内部网络,你的源代码库或者两者。然而,减轻受损服务器机密的损害并避免在源代码中存储机密的系统涉及使用诸如https://zookeeper.apache.org之类的协调服务的机密密钥轮转。使用cron作业每隔几个小时左右生成一个应用程序密码(无论您的访问令牌是否有效),并将更新后的密码推送到Zookeeper。在每个需要知道令牌密钥的应用程序服务器中,配置每当ZK节点值更改时更新的ZK客户端。存储主密钥和次密钥,每次更改密钥密码时,都将新密钥设置为主密钥,将旧密码设置为次密钥。这样,现有的有效令牌仍然有效,因为它们将根据次要机密进行验证。到次级机密被旧的主要机密取代时,所有与次要机密签发的访问令牌都会过期。