使用 JSON Web 令牌的 CSRF 保护

CSRF protection with JSON Web Tokens

我看到在使用 JWT 时,不需要防止 CSRF 攻击,例如:“since you are not relying on cookies, you don't need to protect against cross site requests”。

但是,我不明白的是:如果我将令牌存储在 localStorage 中(正如我被告知的那样 on a tutorial of the same website),是什么阻止了攻击者通过读取我的 localStorage 而不是我的饼干?

由于它是在服务器端生成的,所以我不知道如何在不将令牌存储在客户端某处的情况下将其用于客户端请求。

严格来说,是的,存储在 local/session 存储(我称之为 HTML5 存储)中的任何内容都可能在跨站点脚本 (XSS) 攻击中被盗。参见 this article

但是,有很多活动部件需要考虑。

首先,HTML5 存储和 cookie 在 JavaScript 访问方面的范围存在细微差别。

HTML5 存储为:

  • 分为 http 和 https。存储在 http://example.com HTML5 存储中的项目无法由 JavaScript 运行 在 https://example.com 上访问。
  • 在子域之间划分。存储在 http://example.com HTML5 存储中的项目无法在 http://sub.example.com 上被 JavaScript 运行 访问(你可以做一些 tricks 来解决这个问题,然而)。

饼干更松软:

  • 域为 example.com 的 cookie 将转到 http://example.comhttps://example.com 除非 它具有属性 secure , 在这种情况下它只会被发送到 https.
  • 未使用显式域发送的 cookie 只会被发送回发送它的确切域。如果域被明确定义为 example.com,那么它将被发送到 example.comsub.example.com。 (不幸的是,这是 cookie“规范”中最令人困惑的部分,参见 this article)。
  • 如果 cookie 在具有匹配域的页面上 运行(并尊重 secure cookie 标志),则 JavaScript 可以读取该 cookie 除非 cookie 具有 httpOnly 属性,在这种情况下 JavaScript 将无法读取它。

其次,由于 cookie 标有域,因此当向服务器发出请求时,浏览器将发送具有匹配域的全部且唯一的 cookie,无论域是什么发起请求的页面.

最后一部分是 CSRF 攻击是如何完成的(同源策略的作用有限)。 OWASP page on CSRF 是了解此类攻击如何工作的好资源。

在本地存储中存储身份验证令牌并手动将其添加到每个请求以防止 CSRF 的原因是关键字:手动。由于浏览器不会自动发送该身份验证令牌,如果我访问 evil.com 并且它设法发送 POST http://example.com/delete-my-account,它将无法发送我的身份验证令牌,因此请求被忽略。

考虑到上述情况,是使用 cookie 还是 HTML5 存储成为一系列权衡:

将 authen 令牌存储在 HTML5 存储意味着:

  • (-) 它在 XSS 攻击中被盗的风险。
  • (+) 提供 CSRF 保护。
  • (-) 必须手动修改发送到服务器的每个请求,将您限制为 SPA(例如 AngularJs)Web 应用程序。

另一方面,如果您将身份验证令牌存储在标记为 httpOnly secure 的 cookie 中,则:

  • (+) authn token不能被XSS窃取
  • (-) 您必须自己提供 CSRF 保护。在某些框架中实施 CSRF 保护比在其他框架中更容易。

哪个选项更好取决于您的需要。

  • 您的身份验证令牌是否保护与金钱有关的任何东西?您可能需要 cookie httpOnly secure 选项。
  • 实施 CSRF 保护所需的工作量是否不值得它所保护的资产?那么 HTML5 存储可能是正确的地方。

使用基于令牌的身份验证时,您必须手动将令牌与请求相关联。与 cookie 不同,令牌不会由浏览器自动设置,因此不易受到 csrf 攻击。

虽然这种方法可以免受 csrf 攻击,但它容易受到 xss 攻击。

一个最小的改进是使用 session storage 而不是 local storage,因为 session storage 数据在用户关闭 tab/browser 后被清除。