内容安全策略属性Mvc - 添加多次

时间:2017-08-16 08:35:55

标签: c# asp.net asp.net-mvc content-security-policy

我正在使用ASP.NET MVC 5.我编写了一个小的过滤器属性,用于将内容安全策略添加到响应头。这是代码:

public class ContentSecurityPolicyFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        HttpResponseBase response = filterContext.HttpContext.Response;

        response.AddHeader("Content-Security-Policy", "default-src *; " +
          "img-src * data:; " +
          "style-src 'self' 'unsafe-inline' http://fonts.googleapis.com https://fonts.googleapis.com; " +
          "script-src 'self' 'unsafe-inline' 'unsafe-eval' " +

          "localhost:*/* " +

          "https://facebook.com " + 
          "*.facebook.com " +

          "https://facebook.net " + 
          "*.facebook.net " +

          "https://onesignal.com " +
          "*.onesignal.com " +

          "https://abtasty.com *.abtasty.com *.convertexperiments.com " + 

          "http://www.googletagmanager.com " +
          "https://www.googletagmanager.com " +

          "http://www.google-analytics.com " +
          "https://www.google-analytics.com " +

          "http://www.googleadservices.com " +
          "https://www.googleadservices.com ");

        base.OnActionExecuting(filterContext);
    }
}

到FilterConfig:

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        //...
        filters.Add(new ContentSecurityPolicyFilterAttribute());
    }
}

我的问题是此代码在每个请求中添加了多个Content-Security-Policy标头。它应运行一次,并仅添加此标头一次。我是对的吗?

CSP Header Added Too Many Times

那么我可以解决这个问题呢?

1 个答案:

答案 0 :(得分:5)

要解决此问题,您需要发现为什么多次调用您的过滤器(假设它是!),我发现了一种方式,但它可能不是对你来说是一样的(将一个调试器附加到过滤器的第一行,然后查看调用堆栈,看看是什么触发它)。

我在一个空的MVC项目中使用了以下简化案例(具有默认的HomeController和co。)来验证在最简单的情况下,ContentSecurityPolicyFilterAttribute过滤器只执行一次:

// Truncated CSP filter
public class ContentSecurityPolicyFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        HttpResponseBase response = filterContext.HttpContext.Response;

        response.AddHeader("Content-Security-Policy", "default-src *; img-src * data:; ");

        base.OnActionExecuting(filterContext);
    }
}

// Addition to FilterConfig.cs
filters.Add(new ContentSecurityPolicyFilterAttribute());

使用上面的代码,我只会看到Content-Security-Policy标题的一个实例,即使Index中的HomeController操作更改为:

return RedirectToAction("Contact");

这两个请求都显示(使用Fiddler),请求Index将HTTP 302重定向返回到/Home/Contact,并且两者都只包含一个CSP标头。

如何获得多个Content-Security-Policy标题

Partial Views

如果您正在使用部分视图,那么这很可能是重复标头的原因,特别是如果他们正在调用Controller操作来填充自己。

将以下代码添加到HomeController:

public ActionResult PartialContentForHome()
{
    return View("PartialContentForHome");
}

在名为PartialContentForHome.cshtml的Views \ Shared下创建一个包含以下标记的新局部视图(用于视觉证明,正在调用局部视图):

@{
    Layout = null;
}
<h1>
    Partial!
</h1>

最后,将@Html.Action("PartialContentForHome", "Home")添加到视图文件Views\Home\Index.cshtml中,您:

  1. 获取由Views\Home\Index.cshtmlViews\Shared\PartialContentForHome.cshtml组成的页面,即文本“Partial!”将显示在页面
  2. 将达到在CSP过滤器的第一行设置的断点
  3. 将在发送到客户端的响应中看到两个CSP标头实例
  4. 如何避免使用多个标题

    如果问题是由部分视图/调用控制器操作引起的,则需要确保过滤器将自身调节为仅对每个请求执行一次。一种方法是将值填入HttpContext.Items集合,以用作“我已添加标题”标记,例如:

    public class ContentSecurityPolicyFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (!filterContext.RequestContext.HttpContext.Items.Contains(nameof(ContentSecurityPolicyFilterAttribute)))
            {
                HttpResponseBase response = filterContext.HttpContext.Response;
    
                response.AddHeader("Content-Security-Policy", "default-src *; img-src * data:; ");
                filterContext.RequestContext.HttpContext.Items.Add(nameof(ContentSecurityPolicyFilterAttribute), string.Empty);
            }
            base.OnActionExecuting(filterContext);
        }
    }
    

    在此更新版本的过滤器中,无论何时执行,它都会在HttpContext.Items中查找以其自身命名的条目。如果其中一个不存在,则会添加标头,然后向HttpContext.Items添加一个条目,以便在再次运行时,Content-Security-Policy标头不会重新添加。这将在通用的情况下工作,以确保每个请求不会多次执行过滤器,但我们可以更好地为标头执行,具体来说:

    public class ContentSecurityPolicyFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            HttpResponseBase response = filterContext.HttpContext.Response;
            var header = response.Headers["Content-Security-Policy"];
            if (header == null)
            {
                response.AddHeader("Content-Security-Policy", "default-src *; img-src * data:; ");
            }
            base.OnActionExecuting(filterContext);
        }
    }
    

    即。我们需要做的就是检查标头是否在响应的Headers集合中,如果是,则不要再添加它。