
localStorage
代币类型
- 访问令牌通常是由服务器签名的短期JWT。它们包含在客户端向服务器发出的每个HTTP请求中。令牌用于授权请求。
- 刷新令牌通常是存储在数据库中的长期令牌,用于在前一个令牌过期时获取新的访问令牌。
令牌应确切存储在客户端的什么位置?
有两种在客户端上存储令牌的常用方法:本地浏览器存储和cookie。关于哪种方法更好的争论很多。大多数人倾向于使用Cookie,因为它们具有更好的安全性。
让我们比较一下本地存储和cookie。我们的比较主要基于该材料及其评论。
本地存储
▍优势
本地存储的主要优点是使用方便。
- 使用本地存储非常方便,这里使用纯JavaScript。如果您的应用程序没有后端,并且您依赖于第三方API,则可能无法始终请求这些API为您的网站设置特定的Cookie。
- 使用本地存储,可以方便地使用需要在请求标头中放置访问令牌的API。例如-如下:
Authorization Bearer ${access_token}
。
▍缺点
本地存储的主要缺点是它容易受到XSS攻击。
- 进行XSS攻击时,攻击者可以在您的站点上运行其JavaScript代码。这意味着攻击者可以访问存储在中的访问令牌
localStorage
。 - XSS攻击的来源可能是您网站中包含的第三方JavaScript代码。可能是类似React,Vue,jQuery,Google Analytics(分析)脚本之类的东西。在现代条件下,开发不包含第三方库的站点几乎是不可能的。
饼干
▍优势
Cookies的主要优点是无法从JavaScript进行访问。因此,它们不像本地存储一样容易受到XSS攻击。
- 如果使用标志
HttpOnly
并保护cookie,则表示JavaScript无法访问这些文件。也就是说,即使攻击者可以在您的页面上运行他的代码,他也将无法从cookie中读取访问令牌。 - Cookies在每个HTTP请求中自动发送到服务器。
▍缺点
根据特定的情况,可能会发生Cookie无法存储的令牌的情况。
- Cookie的大小限制为4 KB。因此,如果您使用大型JWT,则将它们存储在Cookie中将不起作用。
- 在某些情况下,您无法将Cookie传递到API服务器。某些API也可能需要在标头中放置令牌
Authorization
。在这种情况下,您将无法在令牌中存储令牌。
XSS攻击
本地存储很容易受到XSS攻击,因为使用JavaScript非常容易。因此,攻击者可以获得令牌的访问权,并利用令牌的优势。但是,尽管HttpOnly cookie无法通过JavaScript访问,但这并不意味着您可以通过使用cookie窃取访问令牌来保护自己免受XSS攻击。
如果攻击者可以在您的应用程序中运行他的JS代码,则意味着攻击者可以简单地将请求发送到您的服务器,并且令牌将自动包含在该请求中。这样的工作方式对攻击者而言根本不那么方便,因为攻击者无法读取令牌的内容。但是攻击者很少需要这个。此外,通过这种工作方案,攻击者使用受害者的计算机而不是他自己的计算机来攻击服务器可能更有利可图。
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
在这种情况下,攻击者可以创建带有隐藏字段的表单,用于输入将POST请求发送到的电子邮件地址
https://site.com/email/change
。在这种情况下,会话cookie将自动包含在此类请求中。
但是,通过使用
SameSite
响应标头和反CSRF令牌中的属性,可以轻松保护这种威胁。
小计
尽管Cookie并非完全不受攻击,但存储令牌的最佳方法是尽可能选择令牌
localStorage
。为什么?
- 本地存储和cookie都容易受到XSS攻击,但是如果使用HttpOnly cookie,攻击者将更难进行攻击。
- Cookies容易受到CSRF攻击的影响,但是可以通过使用属性
SameSite
和反CSRF令牌来减轻此类攻击的风险。
即使您需要使用标题
Authorization: Bearer
或JWT大于4KB,也可以使用Cookies 。这也符合OWASP准则:“请勿将会话ID存储在本地存储中,因为始终可以从JavaScript获得相应的数据。Cookies可以帮助减轻风险HttpOnly
。“
使用Cookie存储OAuth 2.0令牌
让我们简要列出存储令牌的方法:
- 方法1:将令牌存储在本地存储中。此方法容易受到XSS攻击。
- 方法2:将令牌存储在HttpOnly cookie中。此方法易受CSRF攻击,但是可以降低此类攻击的风险。与第一个相比,此令牌存储选项可以更好地防御XSS攻击。
- 方法3:将刷新令牌存储在HttpOnly cookie中,并访问内存中的令牌。就CSRF攻击而言,这种存储令牌的方式更安全,并且可以更好地防御XSS攻击。
下面,我们将仔细研究存储令牌的第三种方法,因为在列出的三种方法中,这是最有趣的。
就CSRF攻击而言,为什么将刷新令牌存储在HttpOnly cookie中更安全?
攻击者可以创建访问的表单
/refresh_token
。响应此请求,返回一个新的访问令牌。但是,如果攻击者使用HTML表单,则无法读取响应。为了防止攻击者成功执行获取或AJAX请求并读取响应,必须正确配置授权服务器的CORS策略,即,使服务器不响应未授权网站的请求。
您如何设置?
步骤1:在验证用户身份时返回访问令牌并刷新令牌
用户进行身份验证后,身份验证服务器将返回
access_token
(访问令牌)和refresh_token
(刷新令牌)。访问令牌将包含在响应正文中,刷新令牌将包含在cookie中。
这是您需要用来设置Cookie来存储刷新令牌的方法:
- 标志
HttpOnly
-防止JavaScript读取令牌。 - 一个标志
secure=true
,将导致仅通过HTTPS传输数据。 - 该标志
SameSite=strict
应尽可能使用以防止CSRF攻击。仅当授权服务器与系统前端属于同一站点时,才可以使用此方法。如果不是这种情况,那么授权服务器必须在后端设置CORS标头,或使用其他方法来确保带有刷新令牌的请求只能由授权网站提出。
步骤2:将访问令牌存储在内存中
将访问令牌存储在内存中意味着将前端代码中的令牌写入变量。当然,这意味着如果用户关闭打开网站的选项卡或刷新页面,则令牌将丢失。这就是为什么我们有一个刷新令牌。
步骤3:使用刷新令牌获取新的访问令牌
如果访问令牌丢失或无效,则需要联系端点
/refresh_token
。在这种情况下,在步骤1中保存在cookie中的刷新令牌将包含在请求中。然后,您将收到一个新的访问令牌,可用于发出API请求。
所有这些意味着JWT可以大于4KB,并且可以将它们放置在header中
Authorization
。
结果
我们这里介绍的内容将为您提供有关在客户端上存储JWT以及如何使您的项目更安全的一些基本信息。
您如何在客户端上存储JWT?
