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.
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.
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
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
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