代码之家  ›  专栏  ›  技术社区  ›  Lost_In_Library

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

  •  3
  • Lost_In_Library  · 技术社区  · 7 年前

    我正在使用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);
        }
    }
    

    要筛选配置:

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

    我的问题是,这段代码添加了多个 Content-Security-Policy 每个请求的标题。它应该运行一次,并且只添加一次此标头。我说得对吗?

    CSP Header Added Too Many Times

    1 回复  |  直到 7 年前
        1
  •  8
  •   Rob    7 年前

    要解决此问题,您需要发现 您的过滤器被多次调用(假设是!),我发现了一种发生这种情况的方式,但对您来说可能不同(在过滤器的第一行附加一个调试器,并查看调用堆栈,以查看是什么触发了它)。

    HomeController 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 中的操作 更改为:

    return RedirectToAction("Contact");
    

    指数 /Home/Contact 并且两者仅包含一个CSP头。

    内容安全策略

    Partial Views .

    如果您使用的是部分视图,那么这很可能是重复头的原因,特别是当它们调用控制器动作来填充自己时。

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

    在“视图\共享”下创建新的局部视图称为 PartialContentForHome.cshtml

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

    最后,添加 @Html.Action("PartialContentForHome", "Home") 进入视图文件 Views\Home\Index.cshtml

    1. 获取由两者组成的页面 视图\主页\索引。cshtml Views\Shared\PartialContentForHome.cshtml
    2. 将达到CSP过滤器第一行上设置的断点 两次
    3. 将在发送到客户端的响应中看到CSP头的两个实例

    如果问题是由部分视图/调用控制器操作引起的,则需要确保过滤器将自身调节为每个请求仅执行一次。一种方法是在 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。项目 HttpContext。项目 因此,如果它再次运行 全体的 案例,以确保过滤器不会在每个请求中执行多次,但我们可以为标头执行一次更好的过滤器,具体来说:

    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);
        }
    }
    

    i、 e.我们需要做的就是检查头是否在响应的 Headers 集合,如果是,则不要再次添加。