Working Principle of Content Security Policy (CSP) in Web Applications

Introduction

Here in this tutorial I am going to discuss about working principle of content security policy (CSP) in web applications. What if you want to minimize attacks on your website, what if you want to restrict the content to be displayed on your web site for some security reasons, what if you want to prevent Cross Site Scripting (XSS) and data injection attacks? Then you need to apply security on your content to prevent such attacks. These attacks may range from your data theft to web site defacement to the distribution of malwares.

Content Security Policy is an additional layer of security that helps to detect and mitigate certain types of attacks (primarily to prevent XSS attacks) as mentioned in the previous paragraph. To enable CSP you need to configure your web server to return Content-Security-Policy HTTP header. The HTTP Content-Security-Policy response header allows web site administrators to control resources the user agent or the browser is allowed to load for a given page.

In the later sections of this tutorial I will show you how to implement Content Security Policy (CSP) in Apache HTTP Server and Nginx Server or even using <meta> tag how you can specify CSP on your view template.

Security Threats

Malicious scripts may be executed by the attackers’ browsers because the content of source is trusted by the browser even if the content is not coming from where it seems to be coming from.

CSP makes it possible for server administrators to reduce or eliminate the weeds by which XSS can occur by specifying the sources of contents (i.e., domains) that the browser will consider to be valid sources of executable scripts. CSP compatible browser then only will execute those scripts coming from the allow listed domains and ignore all other scripts including the inline scripts.

The domain restriction is the first point but you need to also make sure that you as a server administrator specify the protocols to be used are allowed. You should not allow non-secured protocol, for example, HTTP and make sure you always load the content from HTTPS protocol. Apart from this you need to ensure that the cookies are marked with secured attributes only. You should not server any content over HTTP protocol and make sure you place proper redirects from HTTP to HTTPS.

Using CSP (Content Security Policy)

CSP involves specifying the Content-Security-Policy HTTP header to a web page to control what resources the user agent is allowed load for that web page.

content security policy in web applications

For example, you may want to allow only your server’s JavaScript files and no other external JavaScript files to be allowed to be loaded for your web page. You may also want to allow your server’s style sheets to be allowed and from some specified external sources.

Specifying Policy

The CSP can be specified in the following way by specifying the Content-Security-Policy HTTP header:

Content-Security-Policy: <policy directives>

Where <policy directives> is a string containing your content security policy.

Writing Policy

A policy is described using a set of policy directives, each of which describes the policy for a certain resource type. The policy should include a default-src policy directive, which is a fallback for other resource types when they don’t have policies of their own.

For example, a policy needs to include a default-src or script-src directive to prevent inline scripts from running, as well as blocking the use of eval() function. A policy needs to include a default-src or style-src directive to restrict inline styles from being applied from a element or a <style> attribute.

Examples

Here I am going to give you some examples which will help you to build your own CSP header.

The following directive will allow all content to come only from the site’s own origin. This directive will not allow even from the sub-domain of the site.

Content-Security-Policy: default-src 'self';

Let’s say you want to allow content from a domain (roytuts.com) and all its subdomains also:

Content-Security-Policy: default-src 'self' roytuts.com *.roytuts.com;

Let’s discuss first about different CSP properties which can be used for controlling the content to be allowed in the user agent.

  • default-src: an optional method if no other attribute is defined.
  • script-src: locations of external scripts which can be loaded.
  • img-src: locations from which images can be loaded on the page.
  • media-src: locations from which rich media like video, audio can be loaded.
  • object-src: locations from which plugins can be retrieved.
  • manifest-src: locations from which application manifests can be fetched.
  • frame-ancestors: locations from which another web page can be loaded using a frame, iframe, object, embed, or an applet element.
  • style-src: locations from where style sheets can be loaded.
  • font-src: locations from where fonts can be retrieved.
  • frame-src: locations from where frames can be loaded.

There are many other properties which you can find online.

Let’s take another example. Let’s say you want to use Google tag manager on your website and you want to apply CSP on your website then you have to build the following CSP directive:

Content-Security-Policy "default-src 'self'; script-src 'self' https://tagmanager.google.com; style-src 'self' https://tagmanager.google.com https://fonts.googleapis.com; img-src 'self' https://ssl.gstatic.com https://www.gstatic.com; font-src 'self' https://fonts.gstatic.com data:";

Let me explain the above security policy. I have specified the default-src.

I have specified script-src as ‘self’ and https://tagmanager.google.com. So here I am allowing JavaScript to be executed from my own origin as well as from https://tagmanager.google.com.

I have allowed style sheet to be allowed from my own origin as well as from https://tagmanager.google.com and https://fonts.googleapis.com (style-src 'self' https://tagmanager.google.com https://fonts.googleapis.com).

I am allowing images to be fetched from my own origin as well from https://ssl.gstatic.com & https://www.gstatic.com (img-src 'self' https://ssl.gstatic.com https://www.gstatic.com).

Similarly I have allowed fonts to be loaded from Google as well as from my own origin server. Note the whitelisting data protocol (data:):

data: Allows data: URIs to be used as a content source. This is insecure; an attacker can also inject arbitrary data: URIs. Use this sparingly and definitely not for scripts.

Now while CSP is present, your JavaScript variables will evaluate to undefined. Therefore you need to provide ‘unsafe-eval’ directive in the script-src section of the CSP.

script-src 'unsafe-eval' 'self' https://tagmanager.google.com;

Let’s say you have the following inline script and due to CSP your JavaScript variables evaluate to undefined. Therefore you need to add ‘unsafe-eval’ to the script-src directive.

<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-{YOUR-CONTAINER-ID}');</script>

You might have inline JavaScript code which is required to work on certain functionalities of your website. So the above inline script will not work due to the CSP header. Therefore you need to add ‘unsafe-inline’ to the directive script-src.

script-src 'unsafe-inline' 'self' https://tagmanager.google.com;

To use both directives you can combine them as shown below:

script-src 'unsafe-inline' 'unsafe-eval' 'self' https://tagmanager.google.com;

The complete CSP to use Google tag manager would be as given below:

Content-Security-Policy "default-src 'self'; script-src 'unsafe-inline' 'unsafe-eval' 'self' https://tagmanager.google.com; style-src 'self' https://tagmanager.google.com https://fonts.googleapis.com; img-src 'self' https://ssl.gstatic.com https://www.gstatic.com; font-src 'self' https://fonts.gstatic.com data:";

Remember the ‘unsafe-inline’ and ‘unsafe-eval’ could be used with style-src as well.

One way to remove inline script is to move the code into a JavaScript (js) file and include it but that may not be possible all time.

Nonce Based CSP

In stead of using unsafe-inline and unsafe-eval for whitelisting inline scripts/css, you can use nonce (number used once). This is more secure because of random generated number can be used only once. The server must generate a unique nonce value each time it transmits a policy. It is critical to provide an unguessable nonce, as bypassing a resource’s policy is otherwise trivial.

Note the CSP nonce source can only be applied to nonceable elements (e.g. as the element has no nonce attribute, there is no way to associate it with this CSP source).

The nonce value has to be passed to the script-src of Content-Security-Policy as shown below:

Content-Security-Policy "default-src 'self'; script-src 'nonce-{SERVER-GENERATED-NONCE}' 'self' https://tagmanager.google.com";

Then the nonce value can be used in the inline script source as shown below:

<script nonce='{SERVER-GENERATED-NONCE}'>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-{YOUR-CONTAINER-ID}');</script>

CSP Meta Tag

You can use CSP by specifying a http-equiv meta tag in the HTML markup as shown below:

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'unsafe-inline' 'unsafe-eval' 'self' https://tagmanager.google.com; style-src 'self' https://tagmanager.google.com https://fonts.googleapis.com; img-src 'self' https://ssl.gstatic.com https://www.gstatic.com; font-src 'self' https://fonts.gstatic.com data:;">

Apache CSP Header

Add the following CSP to your httpd.conf in your VirtualHost:

Header set Content-Security-Policy "default-src 'self'; script-src 'unsafe-inline' 'unsafe-eval' 'self' https://tagmanager.google.com; style-src 'self' https://tagmanager.google.com https://fonts.googleapis.com; img-src 'self' https://ssl.gstatic.com https://www.gstatic.com; font-src 'self' https://fonts.gstatic.com data:;"

Or in an .htaccess file:

<IfModule mod_headers.c>
	Header set Content-Security-Policy "default-src 'self'; script-src 'unsafe-inline' 'unsafe-eval' 'self' https://tagmanager.google.com; style-src 'self' https://tagmanager.google.com https://fonts.googleapis.com; img-src 'self' https://ssl.gstatic.com https://www.gstatic.com; font-src 'self' https://fonts.gstatic.com data:;"
</IfModule>

You can generate a nonce with Apache HTTP server then your Apache server will pass the generated nonce to your script where you want to use it. You can check the github link how to generate it and use it.

Example

Server config

The following configuration should be done in httpd.conf file.

LoadModule headers_module modules/mod_headers.so
LoadModule cspnonce_module modules/mod_cspnonce.so

# add the CSP_NONCE to the "default-src"
Header add Content-Security-Policy "default-src 'self' 'nonce-%{CSP_NONCE}e';"

Usage in server-side script

Using the CSP nonce is as simple as loading the CSP_NONCE server variable using whatever method is available in your script language of choice. Here’s a dead-simple example in PHP:

<?php

// access the CSP nonce from a script
$csp_nonce = $_SERVER['CSP_NONCE'];

?>

Or, a more realistic example:

<script nonce="<?= $_SERVER['CSP_NONCE'] ?>">
  var inline = 1;
</script>

Nginx CSP Header

Add the following CSP directive to your server{...} block:

add_header Content-Security-Policy "default-src 'self'; script-src 'unsafe-inline' 'unsafe-eval' 'self' https://tagmanager.google.com; style-src 'self' https://tagmanager.google.com https://fonts.googleapis.com; img-src 'self' https://ssl.gstatic.com https://www.gstatic.com; font-src 'self' https://fonts.gstatic.com data:;";

For generating nonce in Nginx server and how to use it, you can read the tutorial.

Unless you know all details of in and out of the website, I would not recommend to use CSP on your web site.

Practical Example on CSP

Now I will show you how to implement CSP on your website. I assume that your website runs on Nginx server.

Let’s start with the basic security directives as given below:

add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; img-src 'self' https://*.gravatar.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com data:; frame-src 'self'; object-src 'none'";

So what I am doing in the above security directives. I am allowing inline script to be executed, only scripts to be loaded from my origin server, images are allowed to be loaded from gravatar.com and from my origin server, styles to be loaded from googleapis.com and from my origin server, fonts to be loaded from my origin server and from gstatic.com.

The above content is written into the location /{...} block of Nginx server under server{...} block in Nginx config file.

Once you change the configuration for CSP then you need to restart your Nginx server. Before restarting your server first check your configuration file is okay using the command nginx -t. Restart your Nginx server using command systemctl restart nginx.service.

Now reload your browser and check the network tab for your URL and verify the Headers in Developer tool. Your headers should have the following content security policy:

content security policy in web applications

Now if you check the Console tab of the Developer tool then you will find there are resources which are getting blocked. For me, these are Google Adsense and Google tag manager because I did not allow them in CSP.

content security policy in web applications

Now I have to allow those script resources to be loaded from the respective URLs. For Google Adsense I need to allow https://pagead2.googlesyndication.com and for Google Tag manager I need to allow https://www.googletagmanager.com. Therefore the updated CSP string should be as given below:

add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.googletagmanager.com https://pagead2.googlesyndication.com; img-src 'self' https://*.gravatar.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com data:; frame-src 'self'; object-src 'none'";

Now again you need to restart your Nginx server to reflect changes you have made to your CSP string.

Now reload your page and check the Console again. You will notice another error on the Console – Some cookies are misusing the recommended “Samesite” attribute. You can check Mozilla to mitigate this issue.

content security policy in web applications

One solution would be to allow secure cookies only using the following directive in the location /{...} block under server{...} block.

proxy_cookie_path / "/; SameSite=None; HTTPOnly; Secure";

Apart from this you might have noticed that there are more URLs which need to be whitelisted through CSP. For example, you need to whitelist URLs https://googleads.g.doubleclick.net, https://partner.googleadservices.com, https://adservice.google.co.in, https://adservice.google.com for script source and the list goes on until all are allowed.

So on each update on your CSP you need to restart your Nginx server and until all errors are fixed you have to allow those URLs in your CSP.

That’s all for now and for more details information and report violation you can read about CSP on Mozilla.

Leave a Reply

Your email address will not be published. Required fields are marked *