LocalStorage 与 Cookies:在前端安全存储 JWT 令牌所需了解的一切
JWT 令牌很棒,但如何在前端安全地存储它们呢?我们将讨论 localStorage 和 Cookies 的优缺点。
我们在上一篇文章中介绍了OAuth 2.0 的工作原理,并介绍了如何生成访问令牌和刷新令牌。下一个问题是:如何在前端安全地存储它们?
访问令牌和刷新令牌回顾
访问令牌通常是短期的 JWT 令牌,由您的服务器签名,并包含在对服务器的每个 HTTP 请求中以授权该请求。
刷新令牌通常是存储在数据库中的长期不透明字符串,用于在令牌过期时获取新的访问令牌。
我应该将我的令牌存储在前端的哪里?
存储令牌的常用方法有两种:文件localStorage
或 Cookie。关于哪种方式更好存在很多争论,大多数人倾向于 Cookie,因为它更安全。
让我们来看看两者之间的比较localStorage
。本文主要基于“请停止使用本地存储”和该帖子的评论。
本地存储
优点:很方便。
- 它是纯 JavaScript 编写的,非常方便。如果您没有后端,并且依赖第三方 API,那么您无法总是要求他们为您的网站设置特定的 Cookie。
- 与需要您将访问令牌放在标头中的 API 一起使用,如下所示:
Authorization Bearer ${access_token}
。
缺点:容易受到 XSS 攻击。
当攻击者能够在您的网站上运行 JavaScript 时,就会发生 XSS 攻击。这意味着攻击者可以获取您存储在 中的访问令牌localStorage
。
XSS 攻击可能来自您网站中包含的第三方 JavaScript 代码,例如 React、Vue、jQuery、Google Analytics 等。您的网站几乎不可能不包含任何第三方库。
曲奇饼
优点:该 cookie 不能通过 JavaScript 访问;因此,它不像 那样容易受到 XSS 攻击localStorage
。
- 如果您使用
httpOnly
Cookiesecure
,则意味着无法使用 JavaScript 访问您的 Cookie。这意味着,即使攻击者可以在您的网站上运行 JavaScript,他们也无法从 Cookie 中读取您的访问令牌。 - 它会在每个 HTTP 请求中自动发送到您的服务器。
缺点:根据使用情况,您可能无法将令牌存储在 cookie 中。
- Cookie 的大小限制为 4KB。因此,如果您使用较大的 JWT Token,则无法将其存储在 Cookie 中。
- 在某些情况下,您无法与 API 服务器共享 Cookie,或者 API 要求您将访问令牌放入 Authorization 标头中。在这种情况下,您将无法使用 Cookie 来存储令牌。
关于XSS攻击
本地存储很容易受到攻击,因为它很容易通过 JavaScript 访问,攻击者可以检索您的访问令牌并在以后使用。虽然httpOnly
无法通过 JavaScript 访问 Cookie,但这并不意味着使用 Cookie 就能免受涉及访问令牌的 XSS 攻击。
如果攻击者可以在你的应用程序中运行 JavaScript,那么他们只需向你的服务器发送 HTTP 请求,即可自动获取你的 Cookie。但这对攻击者来说不太方便,因为他们无法读取令牌的内容,尽管他们很少需要这样做。对于攻击者来说,使用受害者的浏览器(只需发送 HTTP 请求)进行攻击可能比使用攻击者的机器更有利。
Cookies和CSRF攻击
CSRF 攻击是一种强制用户执行非预期请求的攻击。例如,如果某个网站通过以下方式接受电子邮件更改请求:
POST /email/change HTTP/1.1
Host: site.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 50
Cookie: session=abcdefghijklmnopqrstu
email=myemail.example.com
然后,攻击者可以轻松地form
在恶意网站上创建一个,https://site.com/email/change
向其中隐藏的电子邮件字段发送 POST 请求,并且session
cookie 将自动包含在内。
但是,可以通过sameSite
在 cookie 中使用标志并包含反 CSRF 令牌来轻松缓解此问题。
结论
尽管 Cookie 仍然存在一些漏洞,但无论如何,它都是更好的选择localStorage
。为什么?
- 和 cookie都
localStorage
容易受到 XSS 攻击,但当您使用 httpOnly cookie 时,攻击者更难发动攻击。 - Cookies 容易受到 CSRF 攻击,但可以使用
sameSite
标志和反 CSRF 令牌来缓解。 - 即使您需要使用
Authorization: Bearer
标头或 JWT 大于 4KB,它仍然可以正常工作。这也符合 OWASP 社区的建议:
不要将会话标识符存储在本地存储中,因为 JavaScript 始终可以访问这些数据。Cookie 可以使用此
httpOnly
标志来降低此风险。
那么,如何使用 cookie 来保留我的 OAuth 2.0 令牌?
总结一下,以下是存储代币的不同方式:
- 选项 1:将您的访问令牌存储在
localStorage
:容易受到 XSS 攻击。 - 选项 2:将您的访问令牌存储在
httpOnly
cookie 中:容易受到 CSRF 攻击但可以缓解,在暴露于 XSS 方面要好一些。 - 选项 3:将刷新令牌存储在
httpOnly
Cookie 中:可以防止 CSRF 攻击,但 XSS 攻击风险略低。我们将详细讨论选项 3 的工作原理,因为它是三个选项中最好的。
将访问令牌存储在内存中,并将刷新令牌存储在 cookie 中
为什么这对 CSRF 来说是安全的?
虽然表单提交/refresh_token
可以正常工作,并且会返回新的访问令牌,但如果攻击者使用的是 HTML 表单,则无法读取响应。为了防止攻击者成功发起fetch
或AJAX
请求并读取响应,需要正确设置授权服务器的 CORS 策略,以阻止来自未经授权网站的请求。
那么这个设置如何工作?
步骤1:用户认证通过后返回Access Token和Refresh Token。
用户认证成功后,授权服务器会返回一个access_token
和一个refresh_token
。access_token
将会包含在响应主体中,refresh_token
将会包含在 cookie 中。
刷新令牌 cookie 设置:
- 使用该
httpOnly
标志来阻止 JavaScript 读取它。 - 使用该
secure=true
标志,以便它只能通过 HTTPS 发送。 - 尽可能使用此
SameSite=strict
标志来防止 CSRF。仅当授权服务器与您的前端位于同一站点时才可以使用此功能。如果不是这种情况,您的授权服务器必须在后端设置 CORS 标头或使用其他方法来确保刷新令牌请求只能由授权网站发出。
步骤 2:将访问令牌存储在内存中
将令牌存储在内存中意味着您将此访问令牌放入前端站点的一个变量中。是的,这意味着如果用户切换标签页或刷新站点,访问令牌将会消失。这就是我们设置刷新令牌的原因。
步骤 3:使用刷新令牌更新访问令牌
当访问令牌失效或过期时,点击该/refresh_token
端点,步骤 1 中存储在 Cookie 中的刷新令牌将包含在请求中。您将获得一个新的访问令牌,然后可以将其用于 API 请求。
这意味着您的 JWT 令牌可以大于 4KB,并且您也可以将其放在授权标头中。
就是这样!
这应该涵盖基础知识并帮助您保护网站安全。这篇文章由Cotter团队撰写——我们正在为网站和移动应用构建轻量级、快速且无密码的登录解决方案。
如果您正在为您的网站或移动应用构建登录流程,以下文章可能会有所帮助:
- OAuth 究竟是什么?OAuth 2.0、访问令牌以及如何在你的网站中实现它
- 使用 Next.js 通过电子邮件和 JSON Web 令牌 (JWT) 身份验证实现无密码登录
- 以下是如何在不到 15 分钟的时间内将 Cotter 的 Magic Link 集成到您的 Webflow 网站!
参考
在撰写此博客时,我们参考了几篇文章,特别是这些文章:
问题和反馈
如果您需要帮助或有任何反馈,请随时在此处发表评论或在Cotter 的 Slack 频道上与我们联系!我们随时为您提供帮助。
准备好使用 Cotter 了吗?
如果您喜欢这篇文章并希望将 Cotter 集成到您的网站或应用程序中,您可以创建一个免费帐户并查看我们的文档。
文章来源:https://dev.to/cotter/localstorage-vs-cookies-all-you-need-to-know-about-storing-jwt-tokens-securely-in-the-front-end-15id