【问题标题】:在浏览器中存储 JWT 的位置?如何防范 CSRF?
【发布时间】:2015-01-19 22:43:08
【问题描述】:

我知道基于 cookie 的身份验证。 SSL 和 HttpOnly 标志可用于保护基于 cookie 的身份验证免受 MITM 和 XSS 的影响。但是,需要采取更多特殊措施来保护它免受 CSRF 的影响。它们只是有点复杂。 (reference)

最近,我发现 JSON Web Token (JWT) 作为身份验证的解决方案非常热门。我知道有关编码、解码和验证 JWT 的知识。但是,我不明白为什么有些网站/教程告诉如果使用 JWT 就不需要 CSRF 保护。我已经阅读了很多,并试图总结以下问题。我只是希望有人提供 JWT 的更大图景,并澄清我对 JWT 的误解。

  1. 如果JWT存储在cookie中,我认为它与基于cookie的身份验证相同,只是服务器不需要会话来验证cookie/token。如果不采取特殊措施,仍然存在 CSRF 风险。 JWT 不是存储在 cookie 中的吗?

  2. 如果 JWT 存储在 localStorage/sessionStorage 中,则不涉及 cookie,因此无需防范 CSRF。问题是如何将 JWT 发送到服务器。我发现here 建议使用jQuery 通过ajax 请求的HTTP 标头发送JWT。那么,只有ajax请求才能进行认证吗?

  3. 另外,我发现了另外一个blog,它指向使用“Authorization header”和“Bearer”来发送 JWT。我不明白博客所说的方法。有人可以解释更多关于“授权标头”和“承载”的信息吗?这是否使所有请求的 HTTP 标头传输的 JWT?如果是,那么 CSRF 呢?

【问题讨论】:

    标签: security authentication cookies csrf jwt


    【解决方案1】:

    我们需要将 JWT 存储在客户端计算机上。如果我们将它存储在 LocalStorage/SessionStorage 中,那么它很容易被 XSS 攻击所抓取。如果我们将它存储在 cookie 中,那么黑客可以在 CSRF 攻击中使用它(无需读取它)并冒充用户并联系我们的 API 并发送请求以代表用户执行操作或获取信息。

    但是有几种方法可以保护 cookie 中的 JWT 不被轻易窃取(但仍有一些高级技术可以窃取它们)。但是如果你想依赖 LocalStorage/SessionStorage,那么可以通过简单的 XSS 攻击来访问它。

    所以为了解决 CSRF 问题,我在我的应用程序中使用了 Double Submit Cookies。

    双重提交Cookies方法

    1. 将 JWT 存储在 HttpOnly cookie 中,并在安全模式下使用它通过 HTTPS 传输。

    2. 大多数 CSRF 攻击在请求中具有与原始主机不同的来源或引荐来源标头。因此,请检查标题中是否有任何它们,它们是否来自您的域!如果不拒绝他们。如果请求中的来源和推荐人都不可用,那么不用担心。您可以依赖我在下一步中解释的 X-XSRF-TOKEN 标头验证结果的结果。

    3. 虽然浏览器会自动为请求的域提供 cookie,但有一个有用的限制:在网站上运行的 JavaScript 代码无法读取其他网站的 cookie。我们可以利用它来创建我们的 CSRF 解决方案。为了防止 CSRF 攻击,我们必须创建一个额外的 Javascript 可读 cookie,称为:XSRF-TOKEN。此 cookie 必须在用户登录时创建,并且应该包含一个随机的、不可猜测的字符串。我们还将这个数字作为私人声明保存在 JWT 中。每次 JavaScript 应用程序想要发出请求时,它都需要读取此令牌并将其发送到自定义 HTTP 标头中。因为这些操作(读取 cookie、设置标头)只能在 JavaScript 应用程序的同一个域上完成,所以我们可以知道这是由使用我们 JavaScript 应用程序的真实用户完成的。

      李>

    Angular JS 让你的生活更轻松

    幸运的是,我在我们的平台中使用了 Angular JS,并且 Angular 封装了 CSRF 令牌方法,使我们更容易实现。对于我们的 Angular 应用程序向服务器发出的每个请求,Angular $http 服务将自动执行这些操作:

    • 在当前域中查找名为 XSRF-TOKEN 的 cookie。
    • 如果找到该 cookie,它会读取该值并将其作为 X-XSRF-TOKEN 标头添加到请求中。

    因此,客户端实现会自动为您处理!我们只需要在服务器端的当前域上设置一个名为XSRF-TOKEN 的cookie,当我们的API 收到来自客户端的任何调用时,它必须检查X-XSRF-TOKEN 标头并将其与JWT 中的XSRF-TOKEN 进行比较。如果它们匹配,则用户是真实的。否则,这是一个伪造的请求,您可以忽略它。此方法的灵感来自“双重提交 Cookie”方法。

    注意

    实际上,您仍然容易受到 XSS 的影响,只是攻击者无法窃取您的 JWT 令牌供以后使用,但他仍然可以使用 XSS 代表您的用户发出请求。

    无论您将 JWT 存储在 localStorage 中还是将 XSRF-token 存储在非 HttpOnly cookie 中,XSS 都可以轻松获取两者。即使是 HttpOnly cookie 中的 JWT 也可能被 XST method 等高级 XSS 攻击获取。

    因此,除了双重提交 Cookie 方法之外,您还必须始终遵循针对 XSS 的最佳做法,包括转义内容。这意味着删除任何会导致浏览器执行您不​​希望它执行的操作的可执行代码。通常这意味着删除导致 JavaScript 被评估的 // <![CDATA[ 标记和 HTML 属性。

    在这里阅读更多:

    【讨论】:

    • @AranDehkharghani 是的,我想它可以防止重放攻击,特别是如果您更改 JWT 并在每次 API 使用它时使以前的 JWT 过期。这意味着您的 JWT 将变得像一次性密码 (OTP)。您可以以不同的方式使用 JWT,具体取决于您对平台安全性的关注程度。
    • 如您所说,如果一个网站容易受到 XSS 攻击,那么用户被利用只是时间问题。似乎我们正在用显着的复杂性换取很小的安全性提升。
    • @shusson 您必须注意 XSS 和 XSRF 攻击以保护您的 JWT。我不同意您以显着的复杂性换取很小的安全性增加。如果安全很重要,那么您需要尽一切努力避免出现 XSS 漏洞。此方法旨在保护您的令牌免受 XSRF 攻击。但这并不意味着您可以忽略 XSS 漏洞。
    • @ImanSedighi 我不清楚,将 jwt 存储在 cookie 中会增加复杂性,现在必须防止 XSRF。那么为什么不直接使用具有短生命周期令牌的本地存储并专注于防止 XSS 呢?
    • @Royi Namir:如果您使用 10 美元的 SSL 证书,则无需担心 Wireshark 的欺骗!如果网站的安全很重要,那么您应该加密数据并使用 HTTPS 协议。
    【解决方案2】:

    JWT 令牌很受欢迎,因为它们在 OAuth 2.0OpenID Connect 等新的授权和身份验证协议中用作默认令牌格式。

    当令牌存储在 cookie 中时,浏览器会自动将其与每个请求一起发送到同一个域,这仍然容易受到 CSRF 攻击。

    承载身份验证是 HTTP 中定义的authentication schemes 之一。这基本上意味着 YOU 将 (JWT) 令牌粘贴在请求的 Authorization HTTP 标头中。浏览器不会自动为您执行此操作,因此它不适合保护您的网站。由于浏览器不会自动将标头添加到您的请求中,因此它不容易受到 CSRF 攻击,这取决于您的身份验证信息是否自动提交到原始域。

    承载方案通常用于保护通过 AJAX 调用或移动客户端使用的 Web API(REST 服务)。

    【讨论】:

    • @Timespace7 不,JWT 令牌也经常用于本地客户端。 OAuth 2.0 具有专门针对本机(移动)客户端的流程。他们不做的是隐式浏览器身份验证(如 cookie 或基本身份验证。)。
    • 我的意思是,如果您的 API 仅从 Authorization 标头中检索 JWT 令牌,那么它就不会受到 CSRF 的攻击。任何从 cookie 获取令牌的站点或 API 都需要缓解 CSRF。
    • 这是否意味着我们可以有效地将 jwt 存储在 cookie 中,并且如果我们在 Authorization 标头中发送带有它的请求将是安全的?
    • @cameronjroe 您可以将其存储在您的 cookie 中,但前提是您不使用 cookie 进行身份验证(在这种情况下使用您的标头)
    • AJAX 调用也源自浏览器。 JWT 令牌主要用于验证 Web API(提供数据)与用于验证 Web 应用程序(提供标记、图像、css 和 JavaScript)的 cookie
    【解决方案3】:

    现在在 2020 年,只需将 JWT 令牌存储在带有 SameSite=strict 的 cookie 中即可击败 CSRF。当然,也要保留securehttpOnly

    https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite

    【讨论】:

    • 此方法的唯一问题是,您无法在 Safari 14+ 上使用它,并且在 2022 年当您需要 Web 小部件的跨站点 cookie 时,将无法在 Chrome 上使用它。跨度>
    • @beytarovski 您能否详细说明即将到来的问题以及会影响哪些用例?
    • @trademark 简单地说,httpOnly 如果您开发像 Intercom 这样的第 3 方应用程序,cookie 根本不会/不会起作用。因为您的应用程序希望在页面刷新时保持客户端网站中的访问者会话。由于 cookie 无法存储在您的服务器中,因此不再是解决方案 (webkit.org/blog/10218/full-third-party-cookie-blocking-and-more)。
    【解决方案4】:

    存储 JWT 整个问题的另一个角度:

    1. JWT 永远不应存储在您的 localStorage 中
    2. 事实上,它们甚至不应该存储在您的 cookie 中除非您能够实施非常严格的 CSRF 保护

    Checkout this for motivation

    • 作为 id_token 的 JWT 就像您的用户凭据
    • JWT 作为 access_token 就像你的会话令牌

    最安全的选项是内存中Checkout this for a deep dive

    【讨论】:

    • 内存中仍然容易受到 XSS 的影响,不是吗?如果是这样,那么受 CSRF 保护的 cookie 似乎是唯一安全的解决方案。
    【解决方案5】:

    在网络浏览器中,您可以将 JWT 存储在 本地/会话存储cookie 中。两者都有漏洞。您可以选择您喜欢的一个,但您应该将安全作为一个整体来保护,并且应该精心设计流程。如果你只阻止 XSRF 和 XSS,它不会帮助你。这是对您问题的简短回答。

    首先您要防止用户数据被盗。非常有问题的是 XSS 攻击。如果您使用存储,攻击者可以窃取令牌 - 将令牌发送到他的服务器并发出请求以窃取用户数据。如果你使用 httpOnly cookie,他不能窃取令牌,但他可以发送请求(浏览器包括 cookie,如果脚本在同一个域中),读取响应并将用户数据发送到他的服务器。结果是一样的。

    为了防止将数据发送到具有不同域的服务器,您可以使用 Content-Security-Policy 标头。我建议研究所有安全标头和网络安全。好的资源是 OWASP。这个论坛不是要写很多页。

    XSRF (CSRF)

    如果您使用 cookie,那么应用程序很容易受到这种攻击。

    如何预防:

    设置 httpOnly、secured 和 SameSite=strict 标志。您还可以使用不带 httpOnly 的第二个 cookie XSRF-TOKEN 并将其值与此 cookie 一起发送到标头 X-Xsrf-Token 中。但是如果浏览器支持,这可以通过 SameSite 标志解决。

    XSS

    在某种意义上,存储和 cookie 都容易受到 XSS 攻击。使用 javascript 代码,您可以读取存储,并且您可以使用浏览器包含的 cookie 向服务器发送请求,因为您在同一个域中。如果您使用用户输入,您应该转义/清理它们。您还可以使用标头 x-xss-protection。最成问题的是 3rd 方 js 库中的恶意代码,因为您无法逃脱它并且它在同一个域上运行。您可以防止用户数据被您的错误窃取,但这样的代码可能会给您的应用程序和用户带来不同的问题。

    【讨论】:

      【解决方案6】:

      将您的访问令牌存储在内存中,并将您的刷新令牌存储在 cookie 中

      为什么这对 CSRF 来说是安全的?

      虽然向/refresh_token 提交的表单会起作用并且会返回一个新的访问令牌,但如果攻击者使用的是 HTML 表单,他们将无法读取响应。为防止攻击者成功发出 fetch 或 AJAX 请求并读取响应,这需要正确设置授权服务器的 CORS 策略以防止来自未经授权的网站的请求。

      您可以在此处阅读更多信息:

      https://dev.to/cotter/localstorage-vs-cookies-all-you-need-to-know-about-storing-jwt-tokens-securely-in-the-front-end-15id

      【讨论】:

      • 这是一个很好的建议,它比 JWT 更广泛地适用,但适用于任何 oauth 令牌格式。这是我能想到的唯一一种适用于使用 rest-api 端点的 Web 应用程序的安全模型。
      猜你喜欢
      • 2017-10-21
      • 2021-03-28
      • 1970-01-01
      • 2012-05-03
      • 2017-05-04
      • 2012-06-30
      • 1970-01-01
      • 1970-01-01
      • 2012-07-15
      相关资源
      最近更新 更多