构建安全 JWT 身份验证过程的指南?
Posted
技术标签:
【中文标题】构建安全 JWT 身份验证过程的指南?【英文标题】:Guidelines to build a secure JWT authentication process? 【发布时间】:2021-05-02 21:00:17 【问题描述】:最近我需要构建一个简单的 REST API,我阅读了有关最佳实践的不同文章,以尽可能减少我的 Web 应用程序的漏洞。在网上搜索我发现了关于如何实现 JWT 令牌的不同教程,每个教程在某些方面都不同,而且我找不到一个节流的“通用方法”。最后,我实现了对我来说似乎最合理的解决方案,但我想确认这是处理此类身份验证的最有效方式。
开始之前:
注意 CORS 政策已实施 为了签署令牌,我使用了“标准”库 客户端和服务器之间的通信是加密的 (HTTPS)#STEP 1:身份验证后生成令牌:
-
我生成了一个使用 HS256 算法签名的短期访问令牌(15 分钟)和一个“长期”刷新令牌(7 天)(使用的密钥长度:512 位)
此外,我将刷新令牌的 ID 存储在缓存中。
我在响应中发回访问令牌(在客户端我只将它保存在内存中 [Redux]),同时我在 HTTPOnly cookie 中设置刷新令牌以使 XSS 攻击更加困难(并非不可能,我知道:目标是避免/减少窃取 cookie 的可能性)
#STEP 2:授权请求
为了授权请求,我只使用在请求标头中的字段中发送的访问令牌。仅当令牌有效时才允许请求。
#STEP 3:刷新访问令牌
为了刷新令牌,我向服务器发送了一个刷新请求。服务器:
-
检查刷新令牌签名是否有效
检查刷新令牌是否在缓存中(检查是否过期并针对伪造令牌设置另一层防御)
同时生成新的访问令牌和新的刷新令牌,同时撤销之前的刷新令牌(并相应地更新缓存)
在响应中发回访问令牌并在仅 HTTP cookie 中发回刷新令牌
有关刷新过程的更多信息
刷新被调用:
-
访问令牌到期之前
在用户打开/刷新 Web 应用程序的确切时刻。这样,如果刷新令牌仍然有效,则用户会自动通过身份验证并准备好使用应用程序。
请注意,刷新令牌只能用于刷新访问令牌而不执行其他请求(为了避免 CSRF 攻击,cookie 不能用于验证请求,但用户需要加载网站获取访问令牌)
进一步的预防措施:关键操作
刷新令牌有两个过期时间。第一个,对于非关键操作,每次发布新令牌时都会刷新。这样,如果用户继续使用该应用程序,他/她可能会永远保持登录状态。对于关键操作,第二个(持续 3 小时)是“绝对的”。这意味着,每次刷新时,关键操作的“计时器”都不会刷新。
//To make it simpler:
nextToken.criticalExpiration=previousToken.criticalExpiration
在“关键”计时器到期后调用刷新请求时,生成的访问令牌有一个字段为 false,表示执行关键操作的选项。如果为 false,则不允许关键操作(因此用户必须重新进行身份验证才能执行这些请求)
总结:
我想了解此过程是否正确执行或是否会产生漏洞。我知道在应用程序的其他部分和/或外部库中总是可能存在漏洞,但是,我想尽量减少留下可被利用的东西的可能性。
【问题讨论】:
【参考方案1】:您必须创建控制器、用于登录的中间件、生成 jwt 令牌以及刷新令牌。 请参阅:https://stackabuse.com/authentication-and-authorization-with-jwts-in-express-js/ 了解更多详情。
【讨论】:
当然可以,但问题的主题不同。我上面的问题中列出的所有控件都是由中间件执行的。但是,问题的范围在于如何实现身份验证过程。上述步骤是否正确执行?我有没有留下任何空隙?此外,我不需要有关如何执行上述步骤的指南(因为我已经编写了该部分的代码),但我需要对与所用技术无关的身份验证过程进行高级讨论【参考方案2】:我不明白为什么刷新令牌有 2 个过期时间。 我认为一个就足够了,只要确保你使用刷新令牌轮换。
用户登录并获取访问令牌 (AT1) 和刷新令牌 (RT1)。 如果 AT1 过期并且用户使用 RT1,则需要使刷新令牌无效,然后给用户新的刷新令牌,比如说 RT2。
我将 RT1 保存在 redis 中(无效刷新令牌列表)
如果有人盗用 RT1 并使用它,服务器可以检查并知道 RT1 已经在 redis 中。
因此您需要使 RT2 以及属于 RT 1 和 RT2 的所有访问令牌无效。
通过这样做,用户需要重新登录。
见:https://auth0.com/docs/tokens/refresh-tokens/refresh-token-rotation
刷新被调用:
访问令牌到期之前
我同意,或者你可以使用 axios 拦截器,当响应 401(未授权)你可以发送刷新令牌。
我同意
将访问令牌存储在内存中(redux、vuex),或者可能是 webworkers 在 httpOnly cookie 中存储刷新令牌以防止 javascript 读取它。 使用安全 cookie,httpsAuth0 建议将令牌存储在浏览器内存中作为最安全的选项。使用 Web Worker 处理令牌的传输和存储是保护令牌的最佳方式,因为 Web Worker 运行在与应用程序的其余部分不同的全局范围内。使用 Auth0 SPA SDK,其默认存储选项是利用 Web Workers 的内存存储。
从这里:https://auth0.com/docs/security/data-security/token-storage#browser-in-memory-scenarios
请注意,存储在内存中的任何值仍然容易受到 XSS 攻击,只是更难有人获得令牌。 见https://community.auth0.com/t/why-is-storing-tokens-in-memory-recommended/17742/4
另一篇好书:https://indepth.dev/posts/1382/localstorage-vs-cookies
【讨论】:
感谢恶意请求时令牌失效的提示。刷新令牌的两个计时器在访问令牌生成期间使用。我希望如果用户长时间未输入密码,则不允许进行一些关键操作。一个典型的用例?想象一下,您将设备留在了其他人手中,并且您长时间登录了您的服务(非常典型的用例)。现在认为此人试图删除您的帐户。在这些情况下,我希望不允许该操作。这就是双计时器的目的。以上是关于构建安全 JWT 身份验证过程的指南?的主要内容,如果未能解决你的问题,请参考以下文章
使用 HTTP 基本身份验证获取 JWT 令牌的安全性如何?
构建 VSTS 扩展 azure webapp 身份验证 JWT 令牌验证失败