我正在为基于浏览器的 Javascript Web 应用程序构建基于 JWT(JSON Web 令牌)的身份验证机制,使用无状态服务器(无用户会话!),并且我想一劳永逸地知道是否使用存储我的 JWT 令牌在cookie将保护我的令牌免受 XSS 攻击,或者如果没有保护,那么与在我的 Javascript 应用程序中使用浏览器本地存储相比,没有真正的优势。
我在SO和许多博客中看到过这个问题的提出和回答,但我从未见过真正令我满意的答案。
这个问题最初是基于征求意见的基础上提出的——按照我原来的措辞,确实如此。因此,现在让我明确表示,我不想要基于开发人员懒惰等模糊概念的意见 - 这就是基本规则旨在消除的内容。我想要的是一个有证据支持的是/否的答案。任何一个:
-
“是的,可以保护 cookie 免受 XSS 和 CSRF 的影响,具体方法如下” or
- “不,通过保护您的 cookie 免受 CSRF 的侵害,您始终会使其遭受同样类型的 XSS 攻击,而正是这种攻击使得 cookie 成为了一个好主意”
因此,我将重申这个问题,并提出一些简化的基本规则,并提前指出其中的漏洞,以便各位专家能够纠正我的错误。
基本原则
-
您的应用程序是一个 javascript 浏览器应用程序 - 它可能位于 AngularJS 中,但也可能是自定义构建的。它通过 REST 调用与服务器通信。比方说,jQuery $ajax 调用。
-
服务器是无状态的:没有会话管理。
-
该应用程序使用 JWT 作为主要身份验证令牌(OAuth2 术语中的“访问令牌”),并使用秘密签名密钥在服务器上验证它们
-
忽略 cookie 的其他重要优点:浏览器管理、编码质量较差的可能性较小等。对于这场战斗,我想考虑绝对安全性,并假设我们可以胜任对任一机制进行编码。
-
忽略cookie的其他缺点,例如非浏览器应用程序等。对于这场战斗,我们只关心基于浏览器的javascript应用程序。
-
在非 cookie 方式中使用标头还是请求正文来传输令牌并不重要;如果您使用本地存储与会话存储也没关系 - 忽略那里的任何安全差异。是的,我知道技术上 Cookie 使用标头,请忽略它。
简而言之,我们只对比较 browser-handles-tokens 与 your-javascript-handles-tokens 以及比较 XSS 和 CSRF 安全风险感兴趣。
参赛者
在红色的角落里,Auth0 https://auth0.com/blog/2014/01/27/ten-things-you-should-know-about-tokens-and-cookies#xss-xsrf:本地存储击败 Cookie,因为 XSS 比 CSRF 更容易修复
在蓝色的角落里,风暴之路 https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage:Cookie 胜过标头,因为实际上 CSRF 比 XSS 更容易修复。
(下面详细摘录两个论点)
选择的武器
XSS 和 CSRF(我们将互换使用 CSRF 和 XSRF:C 似乎在文档中更流行,X 在代码中更流行)
这是我对攻击类型的超级简化总结:
假设您的无状态、经过 JWT 身份验证的 javascript 浏览器应用程序用于在线银行,而攻击者“Evil Corp”想要提交 AJAX REST 调用,通过冒充您的用户将资金转移到他们的帐户。
XSS(跨站脚本)
(正如 Stormpath 所指出的,有很多攻击向量 - 我会选择一个)
Evil Corp 购买了用于密码输入的漂亮文本字段小部件的 github 帐户权限。他们知道您的银行网站使用它,因此他们会更新它以在您输入密码并按 Enter 键时提交 AJAX 请求,将资金转入他们的帐户。您的构建系统愚蠢地提取更新并投入生产。
CSRF(跨站请求伪造)
Evil Corp 知道您的银行网站使用 cookie 中的 JWT 来验证交易,因此他们编写了一个 Web 应用程序来提交 AJAX 请求以将资金转移到他们的帐户。他们将其托管在自己的 evil.com 网站上,并在您碰巧在另一个选项卡中登录到银行网站时通过电子邮件(网络钓鱼)或其他方式引诱您前往那里。浏览器提交来自villain.com的请求,但会附加您的JWT,因为它会发送到正确的站点:银行。
标准防御
防御 XSS 的方法是要非常小心站点中的代码,这样您就不会让浏览器处理用户输入的内容而不对其进行清理(删除 javascript 和 html)以及所有 3rd 方库(Evil 的文本字段小部件)使用前均经过审查。正如 Stormpath 正确指出的那样,这很难,几乎是不可能的。
针对 CSRF 的防御是使用双重提交 cookie 的形式。这意味着我们的服务器创建一个令牌(安全随机字符串)并将其以可读 cookie 的形式发送到我们的 Javascript 浏览器应用程序(按照惯例将其称为“XSRF-TOKEN”),并且我们的 Javascript 在每个请求的标头或正文中将其发送回。
实际上,双重提交 cookie 只是防御 CSRF 的一种方法,但其他一些方法需要有状态服务器会话,而没有其他方法(我think!)提供更好的保护。无状态性还可以通过将令牌放入 JWT 中并在服务器端将其与标头或正文中的令牌进行比较来实现。
但这种防御的真正 CSRF 破坏质量是,同源策略意味着只有我们的应用程序加载的 javascript我们的域名可以读取该cookie。因此,即使 evilcorp.com 上的 javascript 可以通过其请求发送我们的 cookie,它也不能embed我们的 XSRF-TOKEN 因为它不能read it首先。
To really简化CSRF:
- CSRF attacks工作是因为附加 cookie 的浏览器仅依赖于目的地的请求。
- CSRF defences之所以有效,是因为 Javascript 对 cookie 的访问取决于originJavaScript 的。
Auth0的论点
处理 XSS 比 XSRF 更容易 Cookies 有这个特性
允许从服务器端设置 HttpOnly 标志,以便它们只能
在服务器上访问,而不是通过 JavaScript 访问。这很有用,因为
它保护 cookie 的内容被注入访问
客户端代码 (XSS)。由于令牌存储在本地/会话中
存储或客户端 cookie,它们容易受到 XSS 攻击
让攻击者访问令牌。这是一个合理的担忧,并且
因此,您应该保持令牌过期时间较短。
但如果你
考虑一下 cookie 的攻击面,主要的攻击面之一是
XSRF。现实情况是,XSRF 是最容易被误解的一种
攻击,而普通开发人员甚至可能不理解
风险,因此很多应用缺乏抗XSRF机制。然而,
大家都知道注射是什么。简单来说,如果你允许
在您的网站上输入,然后在不转义的情况下渲染它,您
对 XSS 开放。所以根据我们的经验,保护起来更容易
防御 XSS 胜过防御 XSRF。除此之外,反 XSRF 是
并非每个 Web 框架都内置。另一方面,XSS 很容易
通过使用大多数情况下默认可用的转义语法来防止
模板引擎。https://auth0.com/blog/2014/01/27/ten-things-you-should-know-about-tokens-and-cookies#xss-xsrf https://auth0.com/blog/2014/01/27/ten-things-you-should-know-about-tokens-and-cookies#xss-xsrf
风暴路径的论证
Stormpath 建议您将 JWT 存储在网络 cookie 中
应用程序,因为它们提供了额外的安全性,并且
使用现代 Web 框架轻松防御 CSRF。
HTML5 Web Storage易受XSS攻击,攻击面更大
区域,并且成功攻击后可以影响所有应用程序用户。https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage/ https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage/
Also:
我看到很多关于 cookie 与访问冲突的讨论
代币。虽然我们都被存储会话 ID 的系统所困扰
在 cookie 中,并且该 cookie 不安全,因此会被盗。那
很糟糕,但这不是使用代币的理由。这是避免的一个理由
非安全、非 https cookie。https://stormpath.com/blog/token-auth-spa/ https://stormpath.com/blog/token-auth-spa/
My take
Stormpath 支持 cookie 的论点非常有说服力,但其中有一个漏洞我没有看到他们明确解决:
双重提交 CSRF 防御依赖于这样一个事实:我的 CSRF 攻击者无法访问我的 cookie:其中包含 XSRF-TOKEN 的 cookie。但是,该 cookie 不是和本地存储一样容易受到 XSS 攻击吗?
XSS 漏洞可以运行 javascriptmy域,因此它可以读取与我的 JavaScript 相同的 cookie。 (浏览器不知道这不是我的Javascript)
从另一个角度来看:本地存储与可读 cookie 一样受到同源策略的保护。如果我使用 Auth0 方法,并且 XSS 攻击者知道如何在本地存储中找到我的 JWT 并使用它。同一个攻击者不能使用相同的 XSS 脚本来获取我的 XSRF-TOKEN cookie 并使用它吗?
这两种攻击都要求他们阅读并理解我的 JavaScript 应用程序,但这些内容就在他们的浏览器中。
那么有什么区别呢?一个真的比另一个更安全吗?为什么?