我应该使用HTTP referrer验证还是令牌验证来防止CSRF攻击?

10

我了解如何在ASP.NET MVC Web应用程序中保护网站免受CSRF攻击。他们提到了两种方式来实现这一点,可以通过:

  1. using Token Verification by using <@Html.AntiForgeryToken()> and [ValidateAntiforgeryToken]

  2. using HTTP referrer validation such as:

    public class IsPostedFromThisSiteAttribute : AuthorizeAttribute
        {
        public override void OnAuthorize(AuthorizationContext filterContext)
            {
            if (filterContext.HttpContext != null)
                {
                if (filterContext.HttpContext.Request.UrlReferrer == null)
                    throw new System.Web.HttpException("Invalid submission");
                if (filterContext.HttpContext.Request.UrlReferrer.Host !=
                    "mysite.com")
                    throw new System.Web.HttpException
                        ("This form wasn't submitted from this site!");
                }
            }
        }
    

    and

    [IsPostedFromThisSite]
    public ActionResult Register(…)
    

所以我对于保护我的网站免受CSRF攻击是否需要同时使用这两种方法感到困惑,还是可以选择其中一种方法?

4个回答

10

检查引荐者存在问题。首先,HTTP规范明确允许客户端不发送引荐字符串(出于各种隐私原因)。因此,您的某些客户端可能不包括它。其次,攻击者可以伪造引荐字符串,以使它们看起来像是为了执行成功的CSRF攻击所需的。

使用CSRF验证令牌是一种更强大的方法,也是防范CSRF攻击的首选方法。您可以在OWASP CSRF Cheat Sheet中了解这样做的原因。

我还要指出,您也可以同时采用两种方法。通常希望采用防御深度(DiD)策略,这样攻击者就需要打败多个独立的防御措施才能执行成功的攻击。您可以实现一个弱引荐者检查方法(如果客户端提供引荐者,请确保它是正确的,然后再处理请求;如果引荐者不存在,则继续进行操作),并结合CSRF验证令牌。这样,如果客户端提供了引荐信息,您就可以检查它,同时仍然利用更强大的验证令牌方法。


1
但是,反伪造令牌方法确实可以消除大部分基于CSRF的攻击,但它无法阻止那些试图自动注册(然后发送垃圾信息)用户到你的网站的机器人。那么,我如何100%确保我的网站受到保护呢? - John John
5
@johnG: 这不是CSRF攻击。为了阻止垃圾邮件机器人,你需要的是好的CAPTCHA。(事实上,安全性通过混淆也可以相当有效,至少对于小型网站来说:如果你让你的登录表单与其他人稍有不同,一个通用的机器人就无法理解它。垃圾邮件发送者将不得不花费时间调整他们的机器人来适应你的网站,这在成本上对他们来说并不划算,除非你的网站真的很大、很受欢迎。) - Ilmari Karonen
2
@jeffsix 我喜欢包含“弱”引用检查的想法。那是一个不错的额外层。 - Andrew Barber

4
正如其他答案所指出的那样,仅使用referrer检查是不足够的,您真的应该使用防伪标记。然而,正如@jeffsix所指出的,您可以使用referrer检查作为深度防御(DID)策略,因此攻击者需要打败多个独立的防御才能执行成功的攻击。
下面的ValidateReferrerAttribute属性可用于您的HttpPost MVC操作。如果referrer为空,则不执行任何操作。如果referrer不为空,则检查它是否等于指定的主机名。您只需要在已经使用ValidateAntiForgeryTokenAttribute的地方添加它,因此添加非常简单。
/// <summary>
/// For POST requests, checks that the requests referrer is the current site. This could be used along side the ValidateAntiForgeryToken
/// Note that many clients do not send the referrer, so we do nothing in this case.
/// This attribute can be used as part of a Defence-in-Depth (DID) strategy, so an
/// attacker would need to defeat multiple, independent, defenses to execute a successful attack.
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public class ValidateReferrerAttribute : FilterAttribute, IAuthorizationFilter
{
    /// <summary>
    /// Called when authorization is required.
    /// </summary>
    /// <param name="filterContext">The filter context.</param>
    /// <exception cref="System.ArgumentNullException">filterContext</exception>
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }

        if ((filterContext.HttpContext.Request.UrlReferrer != null) &&
            string.Equals(filterContext.HttpContext.Request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase) &&
            !string.Equals(filterContext.HttpContext.Request.UrlReferrer.Host, filterContext.HttpContext.Request.Url.Host, StringComparison.OrdinalIgnoreCase))
        {
            this.HandleExternalPostRequest(filterContext);
        }
    }

    /// <summary>
    /// Handles post requests that are made from an external source. 
    /// By default a 403 Forbidden response is returned.
    /// </summary>
    /// <param name="filterContext">The filter context.</param>
    /// <exception cref="System.Web.HttpException">Request not allowed.</exception>
    protected virtual void HandleExternalPostRequest(AuthorizationContext filterContext)
    {
        throw new HttpException((int)HttpStatusCode.Forbidden, "Request not allowed.");
    }
}

2
只是为了稍微改进你的代码(并使其更易于重用),你可以从filterContext.HttpContext.Request.Url.Host读取当前主机,这意味着你不需要有"[HOST_NAME_HERE]"这一部分。 - Matthew Steeples
感谢@MatthewSteeples。已更新。 - Muhammad Rehan Saeed

2
HTTP头部中的Referer(错误拼写)不可靠。您不应该依赖它来处理任何重要的事情。引用维基百科CSRF文章的话:
“检查HTTP Referer头以查看请求是否来自授权页面通常用于嵌入式网络设备,因为它不会增加内存需求。但是,省略Referer头的请求必须被视为未经授权,因为攻击者可以通过从FTP或HTTPS URL发出请求来抑制Referer头。这种严格的Referer验证可能会导致浏览器或代理在保护隐私的情况下省略Referer头。此外,旧版本的Flash(9.0.18之前)允许恶意Flash使用CRLF注入生成具有任意HTTP请求标头的GET或POST请求。客户端中类似的CRLF注入漏洞可用于欺骗HTTP请求的引用者。”
引用检查也无法防止持续性CSRF攻击,攻击者成功将恶意链接直接注入到您的网站上。唯一可靠的保护措施是使用防伪令牌。

2
虽然我个人从未使用过它,但我会避免基于HTTP_REFERER的任何内容。我认为现在这种情况并不普遍,但我记得曾经有一段时间互联网安全套件(例如Norton Internet Security)会阻止发送HTTP_REFERER。这只会意味着真正的用户可能无法合法地使用您的站点。

编辑:请参见此问题

我不会指望它是可靠的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接