Lost_In_Library Lost_In_Library - 1 year ago 93
ASP.NET (C#) Question

Content Security Policy Attribute Mvc - Adding Many Times

I'm working with Asp.net Mvc 5. I wrote a small filter attribute for adding Content Security Policy to response header. Here is the code;

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


To FilterConfig;

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


But my problem is, this code adding CSP headers every time when it takes request. It should be work only one, and add this header one time only. Am I right ?

CSP Header Added Too Many Times

So what can I to fix this problem ?

Rob Rob
Answer Source

To fix this problem, you need to discover why your filter is being called multiple times (assuming that it is!), I've found one way that it occurs, but it may not be the same for you (attach a debugger to the first line of the filter and look at the call stack to see what's triggering it for you).

I've used the following reduced case in an otherwise empty MVC project (that has the default HomeController and co.) to verify that in the simplest case, the ContentSecurityPolicyFilterAttribute filter only gets executed once:

// 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());

With the code above, I only see one instance of the Content-Security-Policy header present, even if the Index action in HomeController is changed to:

return RedirectToAction("Contact");

Both requests show (using Fiddler), the request to Index returning a HTTP 302 redirect to /Home/Contact and both only contain one CSP header.

How to get more than one Content-Security-Policy header

Partial Views.

If you are using partial views, then this could well be the cause of the duplicate headers, specifically if they're calling Controller actions to populate themselves.

By adding the following code to HomeController:

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

Creating a new partial view under Views\Shared called PartialContentForHome.cshtml that contains the following mark-up (for visual proof the partial view is being invoked):

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

And, finally, adding @Html.Action("PartialContentForHome", "Home") into the view file Views\Home\Index.cshtml, you:

  1. Get a page composed from both Views\Home\Index.cshtml and Views\Shared\PartialContentForHome.cshtml, i.e. the text "Partial!" will be shown in the page
  2. Will hit a break-point that's been set on the first line of the CSP filter twice
  3. Will see two instances of the CSP header in the response sent down to the client

How to avoid having more than one header

If the problem is being caused by partial views/invoking controller actions, you need to make sure that the filter regulates itself to only being executed once per request. One way to do this would be to stuff a value into the HttpContext.Items collection to use as a "I've already added the header" marker, for example:

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

In this updated version of the filter, whenever it's executed it looks in HttpContext.Items for an entry named after itself. If one isn't present, it adds the header and then adds an entry to HttpContext.Items so that if it's run again, the Content-Security-Policy header doesn't get re-added. This will work in the general case to ensure that a filter isn't executed more than once per request but we can go one better for a header, specifically:

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. All we need to do is check to see if the header is in the Response's Headers collection, and if it is, not add it again.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download