在 Angular 中使用持久 XSRF-TOKEN cookie 的风险

Posted

技术标签:

【中文标题】在 Angular 中使用持久 XSRF-TOKEN cookie 的风险【英文标题】:Risk of using a persitent XSRF-TOKEN cookie in Angular 【发布时间】:2015-08-20 10:10:30 【问题描述】:

这和这个问题有关CSRF Protection for Refresh Token Cookie in SPA

我想使用推荐的XSRF-TOKEN cookie 机制来保护另一个 HttpOnly cookie。对于这种情况,我需要使 XSRF-TOKEN cookie 持久化,因为它必须在重新加载后启动应用程序时可用。 Angular $http 中的默认实现仅在会话 cookie 中查找。

如果我使 cookie 持久化并手动设置 X-XSRF-TOKEN HTTP 标头会有什么风险?

【问题讨论】:

【参考方案1】:

如果我使 cookie 持久化并手动设置 X-XSRF-TOKEN HTTP 标头?

风险在于攻击者最终可能会暴力破解令牌值。

建议每个会话都有一个新的 CSRF 令牌。如果你让这个持久化,那么恶意站点可能会不断尝试提交一个跨站点请求,每次都包含不同的令牌值。最终它会尝试所有的令牌字符组合并成功发出请求。

然而,实际上,用户必须同时且每次都访问恶意网站。记住用户打开的标签并每次自动加载的浏览器可能会发生这种情况。

您还可以构建一些蛮力保护。例如,在使用无效的 CSRF 令牌发出 10 次请求后,您可以中止会话,然后通知用户他们已注销。这将减轻蛮力攻击,但是这会将攻击转换为拒绝服务攻击,因为恶意网站将成功注销用户。因此,您应该通过联系用户并通知他们他们正在访问的站点正试图破坏他们来跟进这一点(您可以检查您的服务器日志以确定 refererorigin 标头)。

【讨论】:

我将为新会话生成一个新令牌,但 cookie 将保留到下一个应用程序启动。所以你是对的,同一个 cookie 的有效期更长,并且有更多的时间来暴力破解令牌。感谢您的评价【参考方案2】:

CSRF 攻击在用户登录的未受保护的站点上起作用。考虑使用会话 cookie C(仅此而已)来识别用户会话的站点 S。这意味着浏览器将在对 S 的每个请求中发送 C。由于会话 cookie 的存在是确认会话所需要的全部内容,因此用户在访问 S 时将登录。这很棒。正是我们想要的。

除了...

假设 S can 是一个可以通过诸如https://S/email-cash?email=recipient@examplecom 之类的 URL 发送现金的网站。一个邪恶的网站 E 可以在它的一个页面中嵌入链接https://S/email-cash?email=ATTACKER@examplecom。现在,当用户在登录站点 S 的同时浏览站点 E 并单击此链接时,他们最终会通过电子邮件向攻击者发送资金。更糟糕的是,这个链接可以在后台用 javascript 执行,所以用户只需要访问站点 E。非常糟糕。

之所以会出现此问题,是因为每个带有有效会话 ID cookie C 的请求都被视为有效请求。解决方案是要求浏览器发送一些它只能从站点 S 获得的 ID。这就是 CSRF 令牌。浏览器无法获取它,除非它是由 S 提供的,并且 S 只会在它提供页面时提供它,而不是用于跨站点攻击。

现在,如果您开始将 CSRF 令牌存储为持久性 cookie,它会破坏整个目的,因为它变成了浏览器可以在跨站点攻击中发送的东西。

【讨论】:

感谢介绍 csrf 的基础知识 :) 但实际问题是关于会话 cookie 和用于存储 CSRF 令牌的持久性 cookie 之间的区别。对于 AngularJs 应用程序中使用的会话 cookie 中的令牌,您的最后一句话是否不正确?【参考方案3】:

如果您选择使用持久性 cookie,您仍然容易受到 CSRF 攻击,因为浏览器会将这些 cookie 与请求一起发送

对于 angularjs,以下是我在 SPA 应用程序中使用的内容; CSRF 令牌由后端服务器生成,并仅在index.html 文件的请求中作为标头传递。从那时起,angular 被配置为在标题中添加令牌 + 在会话 cookie 中 - 对于每个内部 $http.post/delete/put/... 请求

app.config(['$httpProvider', function ($httpProvider)

    $httpProvider.defaults.xsrfCookieName = 'csrftoken';
    $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
]);

测试一下

使用这个小 sn-p 手动测试你的 api:

<!DOCTYPE html>
<html>
   <head>
        <script>
            function csrf(id)
            
                document.getElementById("csrf-form-" + id).submit();
            
        </script>
   </head>
   <body>
        <form method="POST" action="http://127.0.0.1:8080/api/test" enctype="text/plain" target="_blank" id="csrf-form-1">
           <input name='"protected":false, "ignore_me":"' value='test"'type='hidden'>  
        </form>

        <button onclick="csrf(1)">CSRF!</button>
   </body>
</html>

【讨论】:

我的应用从 AppCache 加载,所以这不起作用。我希望 cookie 在第一次请求时可用。我不明白你反对持久性 cookie 的论点。持久性XSRF-TOKEN 和仅会话之间有什么区别?这是我的问题。 Jossef 是正确的,持久性 cookie 仍然让您对 CSRF 开放 此测试将如何设置所需的包含 CSRF 令牌的X-XSRF-TOKEN HTTP 标头?

以上是关于在 Angular 中使用持久 XSRF-TOKEN cookie 的风险的主要内容,如果未能解决你的问题,请参考以下文章

Angular2 - 不同级别的组件之间的持久服务

在 Angularjs 中持久化 iframe

使用 Angular cli 6 项目在工作区中使用 chrome 中的组件 css 文件

Angular:如何在控制台中清除网络(API json)。

使用 SORM 持久化递归数据模型

如何在 Angular 4 中推送到数组的 Observable? RxJS