Manage Content Security Policy from Episerver CMS

CSP security measurement ought to be taken as read on every site in the 2020s. Still not many knows what it is or how to use it in a simple manageable way. This blog post shows why and how you could use it.

icon of user profile

Published 31th of May 2020

Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft to site defacement to distribution of malware.

Another pro using CSP is to prevent editors to unattended add images or services/iframes from other untrusted site or for legal copyright reasons.

The basics in the concept of CSP is that you define rules, what domains are allowed on your site to be requested, and the users browser makes sure to follow these rules.

You may get a browser error in the console if a rule is violated

CSP is designed to be fully backward compatible. Browsers that don’t support it still work with servers that implement it, and vice-versa: browsers that don’t support CSP simply ignore it, functioning as usual, defaulting to the standard same-origin policy for web content. If the site doesn’t offer the CSP header, browsers likewise use the standard same-origin policy.

To enable CSP, you need to configure your web server to return the Content-Security-Policy HTTP header

Some rules example

So the concept is to allow your site to connect to external services, if you use google analytics, google maps, Google fonts twitter, Vimeo and Youtube, you would need to add the following:

default-src 'self';

font-src 'self' data: fonts.gstatic.com;

img-src 'self' data: www.google-analytics.com *.g.doubleclick.net maps.gstatic.com maps.googleapis.com *.twitter.com *.twimg.com *.youtube.com;

script-src 'self' 'unsafe-inline' 'unsafe-eval' platform.twitter.com www.google-analytics.com maps.googleapis.com www.google.com www.gstatic.com *.twimg.com;

style-src 'self' 'unsafe-inline' fonts.googleapis.com dl.episerver.net *.twitter.com *.twimg.com;

frame-src platform.twitter.com www.google.com *.twitter.com *.youtube.com player.vimeo.com;

media-src 'self';

connect-src 'self';

object-src 'none';

‘self‘ meaning same origin domain
data: meaning base64 inline attachment ( This is insecure )
‘none’ meaning disabling: object-src 'none' disabling plugins
Default fallback is default-src
Default https default-src https:
SSL – You may use everywhere https://*.domain.com
'unsafe-inline' meaning allowing inline css/script tags
'unsafe-eval' allowing javascript eval() of strings

Result headers sample site

Administer CSP from inside Episerver

When implementing CSP it might be hard to anticipate what services you’ll use over time, thats why i wanted to make it editable from inside the CMS, so I implemented a TextArea-property (preferably on a settings page)

Why Managing CSP from Episerver

While the site is dynamic, services used on site will also change over time. Thats why it comes very handy to implement it like CMS content. When editors wants to add services it will force to grant what sources are used for that service, and thats a good practise.

If you use a service like GTM – adding services is easy, so this will force someone to grant new services before they are added.

You could easily add some admin permission to that property or add an workflow when editing setting.

Important Pitfall

You could write a rule that makes all CMS UI to stop loading. For example default-src ‘none’ will make it not to load frames. So to prevent this you need a backup plan, the code sample below do have an appsetting flag to turn of CSP globally, and I do also exclude CSP on my Episerver UI Url Segment.

Test and Test, be sure of your rules works as you want.

The code

Add a prop on your settingspage:

[UIHint(UIHint.Textarea)]
[Display(Name = "Security headers - CSP",
GroupName = SystemTabNames.Settings
)]
public virtual string ContentSecurityPolicy { get; set; }

Add this to Global.asax:

protected void Application_PreSendRequestHeaders(object sender, EventArgs e)
    {
        if (HttpContext.Current?.Response.Headers != null)
        {
            HttpContext.Current.Response.Headers.Remove("X-AspNet-Version");
            HttpContext.Current.Response.Headers.Remove("X-AspNetMvc-Version");
            HttpContext.Current.Response.Headers.Remove("Server");

            //if you get (blocked:csp) in browser console, then the Content-Security-Policy is applied and you probably need to add the domain to a rule - regards EMVP Luc Gosso
            string csp =  _settingPage.ContentSecurityPolicy;

            if (!string.IsNullOrEmpty(csp) && 
Request.Url.Segments.Length > 1 && 
Request.Url.Segments[1].ToLowerInvariant()!="episerver/" && 
ConfigurationManager.AppSettings["CSP-Remove"] != "true")
            {
                //default could be "Content-Security-Policy", "default-src 'self'; 
                HttpContext.Current.Response.Headers.Add("Content-Security-Policy", csp.Replace("\n", ""));
            }
        }
    }

Other security header parameters to consider

Ill save this for another blog, but read more about Referrer-Policy and Feature-Policy . Further headers to remove for security reasons are “Server”, “X-AspNet-Version”, “X-AspNetMvc-Version”, “X-Powered-By”, not all can be removed by web.config, see above code snippet how to remove.

<system.webServer>
...
<httpProtocol>
  <customHeaders>
    <add name="X-Frame-Options" value="SAMEORIGIN" />
    <add name="X-Content-Type-Options" value="nosniff" />
    <add name="X-XSS-Protection" value="1; mode=block" />
    <add name="Referrer-Policy" value="no-referrer, strict-origin-when-cross-origin" />
    <add name="Feature-Policy" value="layout-animations 'none'; unoptimized-images 'none'; oversized-images 'none'; sync-script 'none'; sync-xhr 'none'; unsized-media 'none';" />
    <remove name="X-Powered-By" />
  </customHeaders>
</httpProtocol>
</system.webServer>

The result

This is how your headers could look like after doing your homework and implementing safety headers

Check your headers

https://securityheaders.com/ <= you should aim for A+ (no warnings)

Read more

About the author

Luc Gosso
– Independent Senior Web Developer
working with Azure and Episerver

Twitter: @LucGosso
LinkedIn: linkedin.com/in/luc-gosso/
Github: github.com/lucgosso