Content-Security-Policy : 101

Udhayaprakasha
5 min readJun 20, 2021

Hola Reader,

Content-Security-Policy is a header added to the HTTP response which tells the user agent what resources are allowed to load for that page. Content-Security-Policy often referred to as CSP. Click here to know more about it.

How it works?

Content-Security-Policy

The above diagram illustrates the working of CSP. The default-src is set to example.com. If the user agent requests index.js & index.css from the same domain, i.e. example.com, the resources are allowed. But when the user agent tries to load xyz.js from a different domain, it is restricted.

CSP restricts loading assets from a unknown/malicious domain. In this article, you would know the implementation of CSP in your web app.

How can I include CSP to my web app?

We will now see how does the CSP header can be added. I’ve set up a Google App Engine project. You can find the Setup here. You can also clone the repo and see the CSP in action.

public class AppFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (response instanceof HttpServletResponse) {
((HttpServletResponse)response).setHeader("Content-Security-Policy", Commons.CSP_CONTENT); //CSP_Content = "default-src 'self'"
}
chain.doFilter(request, response);
}

@Override
public void destroy() {

}
}

I’ve added the Content-Security-Policy via the Filter, with which all the responses will contain the CSP header. If you’re using the Spring framework, you can configure the CSP in spring-security-config.xml.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<security:http>
<security:headers>
<security:header
name="Content-Security-Policy"
value="default-src 'self' " />
</security:headers>
</security:http>
</beans>

There are multiple ways to achieve this, though. Let us get into other details of CSP.

Say your app loads scripts and stylesheets from different domain and also has some inline scripts. Then you cannot provide the default-src as self. You can also define Javascript source, stylesheet source, etc., using CSP directives can be added along with the default-src. Check out the directive reference here.

First off, start with the following CSP in your app. Later you can include the allowed domains that are required.

default-src 'none';
script-src 'self';
script-src-elem 'self';
font-src 'self';
img-src 'self';
style-src 'self';
style-src-elem 'self';
object-src 'self';
connect-src 'self';
report-uri https://reporturl.com

You can see I’ve added a report-uri directive. Its role is to instruct the browser to POST a report of policy failures to the mentioned URL. Instead of the Content-Security-Policy header name, you can use Content-Security-Policy-Report-Only as the header name. The HTTP header name will instruct the browser only to send a report and not to block any. You can also use Sentry as your report only URI. Check out this article for more info.

You can find the blocked scripts/styles in the provided report-uri. Later you can add it to the respective directive. Say you found a script loads from a CDN. Then you can add it to the script-src directive, which tells the browser that script from xyzCDN.com is allowed/trusted.

script-src 'self' https://xyzCDN.com;

Once you have included all the domains, you can change Content-Security-Policy-Report-Only to Content-Security-Policy.

Injecting malicious script using dev tools

Now, let us look into a few more info on the script-src directive.

script-src : unsafe-inline & unsafe-eval

We know that this directive defines the valid sources of Javascript.

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

unsafe-inline: Allows to load inline scripts, inline event handlers and javascript: URIs

unsafe-eval: Allows unsafe dynamic code evaluation i.e. eval()

To get rid of unsafe-inline, you must define nonce. Using a nonce, you can allow the execution of trusted inline scripts.

script-src 'nonce-{random_value}' 'unsafe-eval' 'self' https://xyzCDN.com;

You should generate a random value and replace it with {random_value} in the above directive definition. The generated nonce should be added to the script.

Note: The nonce value should be used only once for one request. The value should be random that no one could guess it.

Check the below GIF to know how to inspect a CSP error from developer tools.

Inspect CSP error using dev tools

Look how helpful it is to navigate to a particular script for which we need a nonce for execution.

If unsafe-inline isn’t specified, the inline event handlers (onclick, onerror,..) and javascript:void(0) mentioned in an anchor tag’s href attribute will also be blocked.

You can also find the nonce implementation here.

How to add nonce to GTM custom event script?

If your app has custom Google Tag Manager HTML tags, then you should define the nonce within the script and use it inside your custom scripts.

<script nonce='<%=nonce%>'>
var nonce = '<%=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','myNewName','GTM-XXXX');
</script>

Now you have to create a variable in tag manager and add them to your script as mentioned above. Follow the below steps.

  1. Sign in to tag manager. Navigate to the Variables tab and create a Javascript Variable as “nonce”.
Tabs in Tag Manager

2. Navigate to the Tags tab and select the Custom HTML Tag for which you want to include the nonce. Click on edit icon.

Custom HTML Tag

3. Then add the nonce to the script and check the Support document.write option as in the image below.

Customer HTML Tag Edit

Conclusion

That’s all folks. I hope you would’ve learned or refreshed some info about Content-Security-Policy. Using this you can control the assets that can be used on your web app.

Thanks for reading!

--

--

Udhayaprakasha

I’m a Software Engineer. Hands-on experience on Java, Spring, Javascript, React-native, React and Web Security.