JWT认证&刷新令牌实现

时间:2017-01-12 13:18:39

标签: rest security authentication jwt

我正在使用自己的身份验证和授权机制开发REST应用程序。我想使用JSON Web Tokens进行身份验证。以下是有效且安全的实施吗?

  1. 将开发REST API以接受用户名和密码并进行身份验证。要使用的HTTP方法是POST,因此没有缓存。此外,在运输时将有用于安全的SSL
  2. 在身份验证时,将创建两个JWT - 访问令牌和刷新令牌。刷新令牌的有效期将更长。这两个令牌都将用cookie编写,以便在每个后续请求中发送它们
  3. 在每个REST API调用中,将从HTTP标头中检索标记。如果访问令牌未过期,请检查用户的权限并允许相应的访问权限。如果访问令牌已过期但刷新令牌有效,请重新创建新的访问令牌并刷新具有新到期日期的令牌(执行所有必要的检查以确保用户的身份验证权限未被撤销)并通过Cookies发送回来
  4. 提供一个注销REST API,它将重置cookie,因此在登录完成之前,后续的API调用将被拒绝。
  5. 我对刷新令牌的理解是:

    由于存在刷新令牌,我们可以缩短访问令牌的有效期,并经常(在访问令牌到期时)检查用户是否仍有权登录。

    如果我错了,请纠正我。

4 个答案:

答案 0 :(得分:10)

  

将开发REST API以接受用户名和密码   身份验证。要使用的HTTP方法是POST,以便在那里   没有缓存。此外,还有SSL用于安全性   中转

这是最常用的方式,所以你在这里很好。

  

在身份验证时,将创建两个JWT - 访问令牌   并刷新令牌。刷新令牌的有效期将更长。这俩   令牌将用cookie编写,以便每次都发送   后续请求

将令牌存储在cookie中我自己并不危险,但是如果你以某种方式让服务器上的JWT模块从那里读取它们你很容易受到CSRF攻击,任何网页都可以触发用户浏览器发送表单+你站点cookie到您的服务器,除非您使用CSRF令牌。因此,通常它们存储在localStorage中,并且每次都“手动”添加到请求标头中。

  

在每次REST API调用中,都将从HTTP检索令牌   头。如果访问令牌未过期,请检查权限   用户并允许相应的访问。如果访问令牌已过期   但刷新令牌有效,重新创建新的访问令牌并刷新   具有新到期日期的令牌(进行所有必要的检查以确保   用户的身份验证权限未被撤销)并通过发回   饼干

除了饼干危险之外,这似乎是安全的。

  

提供一个注销REST API,它将重置cookie,从而   后续API调用将被拒绝,直到登录完成。

您甚至不需要进行API调用,只需清除cookie或localStorage对象,并确保客户端不会丢失令牌。

express-jwt模块的标准要求令牌位于其自己的“授权:承载[令牌]”标题中,我强烈推荐使用cookie。 localStorage API一直可以追溯到IE8,所以你应该很好。

修改

首先了解XSS和CSRF攻击之间的区别非常重要,因为它们通常被认为是相同的。

XSS是指用户在其他用户浏览器中运行您的域时不安全的JS,当发生这种情况时,localStorage中的JWT或cookie中的会话和JWT都不安全。使用cookies上的httpOnly标志,您无法直接访问它们,但浏览器仍会向您的服务器发送带有AJAX请求的浏览器。如果发生这种情况,你通常会运气不好。为防止这种情况发生,请确保在发送到浏览器时转义所有用户输入。

如果您使用脚本标签或iframe加载第三方JS,这可能会危及localStorage,除非您小心,但我还没有做足够的工作来帮助您。

CSRF仅在其他域尝试通过让浏览器自动发送cookie时将普通HTML表单发送到您的服务器时。框架通过将唯一的随机字符串作为隐藏字段插入并在提交时再次检查它来防止这种情况。 JWT在localStorage中是安全的,因为每个域都有自己独立的localStorage区域。

但最终所有这些取决于您的服务是否将使用单个域,在这种情况下,httpOnly cookie将是足够安全且更容易设置,但如果您想将服务扩展到多个域,如api.domain。 com + app.domain.com或添加一个本地应用程序,你被迫将你的JWT存储在localStorage或其他一些本机存储区域。

希望这有帮助!

答案 1 :(得分:1)

  

我对刷新令牌的理解是:

     

由于存在刷新令牌,我们可以缩短访问令牌的有效期,并经常(在访问令牌到期时)检查用户是否仍有权登录。

     

如果我错了,请纠正我。

假设您正在谈论在OAuth中使用JWT作为承载令牌(我强烈建议您遵循OAuth 2.0协议),那是对的。

在JWT中使用额外的身份验证时间(身份验证时间戳)声明,您甚至可以删除第二个令牌并将您的访问权限作为刷新令牌发送(如果auth-server可以发出新的访问令牌,则令牌有效且在允许的范围内具有身份验证时间... ...但是,确定,遵循标准也很好;)

无论如何,在使用JWT作为刷新令牌之前,你应该考虑一些额外的方面(往往会变得困难或甚至违背JWT的基本思想),因为这基本上意味着你引入了长寿JWT:

  • 您是否需要按主题强制用户注销/令牌撤销(例如,如果用户被识别为欺诈)?
  • 您是否需要撤销特定令牌(例如,如果用户丢失设备)?
  • ...

根据您的使用情况,您应该考虑所有可能的影响,长期生命令牌,因为他们通常要求您在服务器端引入某种状态(例如,允许撤销/黑名单)。请记住,JWT概念的美感和安全性在JWT中是短暂的。

答案 2 :(得分:1)

像 OP 一样,我一直在使用资源所有者密码授予。

我从 Saptarshi Basu 的其他 answer in a different post 学到了很多我认为任何研究 OAuth 代码流的人都应该看看它,它概述了一种非常可靠的 SPA 和资源服务器身份验证方法。它主要依靠您的后端(资源服务器)来处理身份验证提供程序作为私人客户端的身份验证。

但是,我只想补充一点,考虑使用 SPA 实施身份验证的人也应该考虑 OAuth Code Flow with PKCE。 PKCE 的主要目标是允许公共客户端(例如 SPA)直接与身份验证提供程序进行身份验证。 PKCE 添加的所有内容是,当 SPA 应用程序启动身份验证时,当用户通过身份验证时,会将散列值发送到身份验证提供程序。在用户通过授权提供者进行身份验证后,它会将用户重定向回具有该散列值和授权代码的 SPA。现在,对于 SPA 调用身份验证提供程序以交换令牌代码的下一部分,它必须提供最初用于创建散列值的密钥,而不是提供客户端机密。这种机制保证了代码不能被截获代码的人使用,而且 SPA 不需要像服务器端应用那样存储客户端秘密。

现在我唯一不确定的是哪个技术上更安全,使用标准代码流进行服务器端身份验证而不使用 PKCE 或直接使用 PKCE 进行 SPA 身份验证?我可以在网上找到的大多数资源目前都描述并推荐了后者。但是我觉得让私人服务器端客户端处理身份验证(如 Saptarshi Basu 所述)可能仍然更安全。我也很想听听他对此的看法。

答案 3 :(得分:0)

两年前我问了这个问题,也接受了答案。但是,根据最近两年的经验和研究,我想回答这个问题,以防万一有人偶然遇到同样的问题。

问题中提到的方法类似于OAuth 2.0的“资源所有者密码凭据”授予类型。但是,我认为最好使用“授权代码授予”类型和Cookie来存储令牌,而不要使用浏览器localStoragesessionStorage。我在this StackOverlow answer中详细说明了原因,实现要点,安全注意事项和参考。