网页应该如何应对 CSRF 攻击?

信息安全 事件响应 csrf
2021-08-17 04:46:29

我知道你使用token来防止CSRF,但是接收页面在被攻击时应该如何响应呢?它是否应该默默地失败,假装交易成功?显示错误信息?记录攻击?

请解释您回答的理由,因为我想了解最佳实践背后的原因。

编辑

到目前为止,一些答案似乎主张向用户提供某种错误消息。当我问这个问题时,我没有想到的一个部分是误报的可能性。也许我只是没有足够的想象力,但我很难创造一个合法用户在正常使用网站的过程中被误认为是 CSRF 攻击的场景。谁能解释这怎么会发生?如果 CSRF 实际上没有在进行中,CSRF 错误消息将如何出现?

如果用户可能意外触发可见的 CSRF 错误消息,我想我可以看到这一点,但如果他们不能,我不确定您为什么要显示一个。大概攻击者会知道他们正在攻击,所以给他们一个错误消息似乎没有意义,或者给他们比他们应该拥有的更多信息。如果有人可以澄清,它将帮助我更全面地理解它。

3个回答

如果您使用秘密令牌解决方案来应对 CSRF 攻击。如果您在请求中没有收到您期望的秘密令牌,可能会有很多响应策略(当您应该包含/排除令牌时,我不会讨论)。您需要在可用性和安全性之间保持平衡

对用户的一些可能的响应可以是

  • 使会话无效并将用户带到通用会话到期页面。此页面可以是空闲超时、使用后退/刷新按钮等的常用页面。对于事务性网站、处理非常敏感数据等网站,应考虑使用此页面。

  • 不要使会话无效,请用户租用密码

  • 在处理请求之前将用户重定向到确认页面

在所有情况下,您都应该将其记录为安全事件。

我通常建议所有异常和错误都显示一个标准错误页面,它不应该揭示错误/异常发生的原因——或者错误是什么。记录错误,并为用户提供日志事务号。

我不愿透露发生了什么的原因,即由于潜在的 CSRF 攻击而拒绝此操作,是因为攻击者获得了有关站点如何防止此类攻击的信息。(当然,在 CSRF 的具体情况下,这可以通过代币的出现来揭示,但我相信在任何情况下,您都不应该透露比绝对必要更多的信息)

这里似乎有两种驱动力。你想告诉最终用户什么,你不想告诉攻击者什么。您可以披露的最具破坏性的信息可能是与错误输出相关的任何时间信息。

再说一次,在一些 CSRF 攻击中,错误是可见的,而在其他攻击中,错误可能被抑制或隐藏。这会让我想在高安全性站点中执行这些操作:

  • 终止会话,以及可能与 OpenID 或联合相关的任何“上游”会话。

  • 如果用户使用多个选项卡登录,请将他们重定向到通知他们的页面:

    • 这可能是第二个浏览会话/选项卡打开的结果。
    • 告诉用户避免再次发生这种情况,关闭所有浏览器,一次只浏览一个站点(或使用本机支持此安全性的浏览器)
    • 发生了什么事(坏人试图对 yyy 做 xxxx)(小心不要回显来自攻击者的数据,否则这会让你面临 XSS)

起初,我喜欢这里的建议,即进入“编辑”屏幕,允许用户验证正在尝试的更改。然后我想到了点击劫持的可能性,以及这如何消除它的任何好处。再说一次,我不想在我的网站上启用不受支持的“功能”,外部用户可以代表某人预先填写表格。如果需要,我会为此目的提供一个专用端点。

到目前为止,我在处理错误时喜欢的其他操作包括:

  • 遇到以下任何趋势的节流 (Thread.Sleep) 连接

    • IP 地址来源的错误(如果来源是经过 NAT 的用户,后面有数千台机器,请小心)
    • 推荐人的错误(小心防止系统范围的 DOS)
    • 目标页面(控制器/视图)(是的,这也可以 DOS)
    • 登录用户(或匿名用户)

    如果您注意到这些因素的组合导致您的许多 CSRF 警报,您可以减慢并限制或拒绝连接,直到情况改善。一个指标可能是错误/时间。

尽管大多数实现都允许使用种子,但 ASP.NET MVC 尤其可以轻松地在整个站点中实现常量(编译入)种子。constant可能会鼓励攻击者尝试猜测您的种子是什么。为了防止这种情况,我粘贴了一些 ASP.NET MVC 代码来解决一些问题

  • 启用动态种子
  • 从所有控制器启动日志记录和异常跟踪的集中位置。
  • 通过在生成错误并将其发送到客户端之前休眠一段固定时间来防止加密 Oracle 泄露。这当然是受到 Scott Gu 的 ASP.NET 缓解措施的启发,增加了固定时间与随机时间的睡眠时间。

首先,请查看此博客以了解基本实现然后OnAuthorization像这样修改方法

    protected override void OnAuthorization(AuthorizationContext filterContext)
    {
        TimeSpan maxSleepDuration = new TimeSpan(0, 0, 0, 0, 700); //Errors will always take 700ms
        DateTime timeStart = DateTime.UtcNow;

        base.OnAuthorization(filterContext);

        string httpMethodOverride = filterContext.HttpContext.Request.GetHttpMethodOverride();
        if (this._verbs.Verbs.Contains(httpMethodOverride, StringComparer.OrdinalIgnoreCase))
        {
            try
            {
                this._validator.OnAuthorization(filterContext);
            }
            catch (HttpAntiForgeryException e)
            {
                //todo: Custom actions here?  Track the IP? Referrer? Do special actions based on IP?

                TimeSpan timespent = DateTime.UtcNow - timeStart;
                int sleepDuration = maxSleepDuration.Milliseconds - timespent.Milliseconds;
                if (sleepDuration > 0 && sleepDuration != System.Threading.Timeout.Infinite)
                    System.Threading.Thread.Sleep(sleepDuration);


                //todo: Error here, or redirect back to validation screen
                throw e;
            }
        }
    }

最后,您可能希望验证您的MachineKey 未设置为autogenerate ,并确保在您的 ITOps 计划中轮换此密钥。