/jul 30, 2018

Fasten your Helmet.js (Part 2): Locking down your Content-Security-Policy

By Bipin Mistry

In our previous post, we discussed the importance of securing your HTTP headers and how Helmet.js can make this easy for apps that use Express.

Helmet.js’s Github page has a wealth of documentation on how to tweak different security header configurations. For this post, we’ll focus on tuning the CSP header. CSP headers can prevent unwanted injection of JavaScript and other files onto your webpage.

What is Content-Security-Policy (CSP)?

CSP instructs the browser how to process certain directives (e.g., code/configurations that instructs the browser to include resources onto the webpage). It was designed to help minimize the impact of attacks that exploit cross-site scripting vulnerabilities. Cross-site scripting is a vulnerability that allows attackers to inject unwanted and/or malicious JavaScript onto a webpage. By default, browsers that do not see a CSP header in an HTTP response will accept all directives, including resources from external domains.

Helmet.js does not add a CSP header as part of it’s default configuration. Because CSP can block the inclusion of files and resources from legitimate third-parties (like CDNs), browsers default to an open and permissive CSP to avoid breaking website functionality or negatively impacting user experience.

Many modern web apps include JavaScript, CSS, fonts, and other resources from different domains. For example, as a developer, you may decide to use a third-party analytics service that requires you to install a small JavaScript file from another domain. Or, you may choose to include Bootstrap files from a CDN. In reality, there are dozens of use cases for including files and resources from external domains.

CSP does not fix cross-site scripting vulnerabilities, but it can help limit the impact of one. As a developer, it’s important to understand how to configure and use CSP as an additional layer of defense-in-depth for your Express app.

CSP Saves the User: An Example

Let’s walk through an example of CSP preventing the exploitation of a cross-site scripting vulnerability.

We have a simple website that allows users to add their name to raffle. It looks like this:

Blog Enter Raffle Form

This site is built on jQuery and Bootstrap, and includes directives for downloading JavaScript, CSS, and font files from CDNs. Here are the CDNs we use:

  • Bootstrap JavaScript, CSS, and font files: maxcdn.bootstrapcdn.com
  • jQuery JavaScript files: code.jquery.com

Unfortunately, our website does not have a CSP header configured, and also has a persistent cross-site scripting vulnerability. The cross-site scripting vulnerability allows any user to submit <\script> tags into the text field and have it displayed on the webpage. These <\script> tags may contain directives to malicious JavaScript on external domains. Worst of all, this vulnerability affects all users who visit the webpage since it’s persistent in nature - once injected it stays on the page permanently!

As you can see below, evil_script.js is downloaded and run in the user's browser! Blog Evil Script Downloaded

Configuring CSP

If we had CSP enabled, our users would not have be subject to the malicious JavaScript from i-am-an-evil-website.hack. To help our users avoid exploitation, Helmet.js gives us a flexible way to configure the CSP to only accept resources from specific domains.

There are many ways to configure CSP, but here are two options below:

  1. Allow resources from your domain only:
    app.use(helmet.contentSecurityPolicy({
    				  directives: {
    				    defaultSrc: ["'self'"]
    				  }
    				}));
    				

    The CSP header will look like this:

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

  2. Allow resources from your domain only, with an exception for specific CDNs we use and trust:
    app.use(helmet.contentSecurityPolicy({
    				 directives: {
    				   defaultSrc: ["'self'"],
    				   scriptSrc: ["'self'", 'code.jquery.com', 'maxcdn.bootstrapcdn.com'],
    				   styleSrc: ["'self'", 'maxcdn.bootstrapcdn.com'],
    				   fontSrc: ["'self'", 'maxcdn.bootstrapcdn.com']
    				 }
    				}));
    				

    The CSP header will look like this:

    Content-Security-Policy: default-src 'self'; script-src 'self' code.jquery.com maxcdn.bootstrapcdn.com; style-src 'self' maxcdn.bootstrapcdn.com; font-src 'self' maxcdn.bootstrapcdn.com

The second option works best for us, since we’ve chosen to include JavaScript, CSS, and font resources from a CDN. You'll also notice that we are using four different directives in our Helmet.js code:

  • defaultSrc: specifies the default whitelist for loading content such as JavaScript, images, CSS, fonts, etc. The self directive permits loading from the same origin.
  • scriptSrc, styleSrc, fontSrc: specifies a whitelist of valid sources/domains for loading scripts, CSS, and fonts, specifically.

A full list of CSP directives can be found here

After enabling CSP, evil_script.js is blocked but Bootstrap and jQuery files from the CDNs are permitted.

Blog CSP Blocks Evil Script

Next Steps

As stated before, Helmet.js’s documentation does a thorough job detailing the different CSP configuration options. There are many other ways to configure your CSP head, so check out this page.

Want a demo of Veracode Interactive Analysis?

Veracode Interactive Analysis (IAST) helps teams instantly discover vulnerabilities in their applications at runtime by embedding security into their development processes and integrating directly into their CI/CD pipelines. Get a demo.

Related Posts

By Bipin Mistry

Bipin Mistry is Sr. Director of Product Management for WAS/IAST product line.  Prior to joining Veracode he was VP Product Management for NEC/Netcracker in their SDN/NFV and Security business unit.  At NEC/Netcracker Bipin’s primary focus is to develop solutions and architectures specifically mapped to NFV/SDN and Orchestration. He has over 28 years expertise in Security, Software Architectures, Mobile and Core Networking Technologies, Product Management, Marketing, Engineering and Sales.  Prior to joining NEC/Netcracker Bipin was VP President of Product Management for a security startup in the field of DDoS analysis and mitigation.  Bipin has also held architectural and management roles at both Juniper Networks (Chief Mobile Architect) and Cisco Systems (Sr. Director of SP Architecture).

Bipin lives Shrewsbury MA with his wife and 2 children.  In his spare time Bipin is a keen runner and is currently attempting to learn Spanish.