Content Security Policy and Drupal

Parent Feed: 

What is CSP?

Content Security Policy – or CSP – is a security feature of modern browsers. Browsers will ignore data from domains that are not cleared in the CSP http-response header. For instance if you embed a YouTube movie on a webpage and the domain is not whitelisted in the CSP header, then the movie will not be loaded. All traffic from youtube.com will be blocked and the movie cannot be displayed.

This is a safety feature; it is not possible to somehow hack the connection or page and add a malicious script from an unknown domain. Indeed the domain is not cleared and all traffic from it will be blocked by the browser and no harm can be done.

You can set the CSP header in your webserver configuration. This can easily become complicated. Not the configuration itself, but setting up and maintaining a list of domains, grouped by type of request, maybe even specified per subdomain, protocol or port can be a daunting task.

Find violations with browser console

A helpful feature of browsers is that they show reports of CSP violations. Go to your Inspector and check Console messages for Security notifications. These messages tell you the exact directive that is violated. The Drupal watchdog will also log violations, more on that later.

How to use CSP in Drupal

Drupal wouldn’t be Drupal if there wasn’t a module for this. It will help you set up the CSP response-headers and give you separate fields for the type of request. Maintaining these can still be overwhelming, though. There are a few tricks to lower the burden, but both the developer and the content editor need to be aware of the complications.

Module

The module is called Security Kit and can be downloaded from https://www.drupal.org/project/seckit. As the name implies it does more than setting CSP headers, but it’s the task we’re concentrating on in this post. There is a stable version for Drupal 7 available, and development for Drupal 8 is underway (alpha2 at the time of writing).

When installed, a configuration page is available under Configuration > System > Security Kit (/admin/config/system/seckit). There is quite a lot of explanation on the page available. What we’re looking for is the first section under “Cross Site Scripting.” There are two options to Enable CSP or use Report only. More on this later on.

Directives

Let’s first look at the directives. A directive is a specific kind of asset that is loaded, the following are available:

  • default
  • script
  • object
  • style
  • img
  • media
  • frame (deprecated)
  • child
  • font
  • connect

The Security Kit gives good examples of what kind of HTML-elements are triggered by which directive. Some are self-explanatory, others are a little less obvious to comprehend.

Sources

Finding the directives are violated is rather easy, use the browser console or see the Drupal watchdog. Think carefully about the domain that is white-listed. You can use wildcards but you might want to list only very specific domains. It is up to you to choose between https://google.com or use google.*. You can also specify sources by scheme (data:, https:), where “data:” can be used for loading base64 encoded images.

Apart from using domains to whitelist sources, there are also a few reserved keywords. The most relevant are:

  • 'self'
  • 'unsafe inline'
  • 'unsafe eval'

'self'

This is the current domain. It is convenient as you don’t have to add all local, development, test, acceptance and preproduction domain names. This also comes in handy when using the domain module.

'unsafe-inline'

Mainly relevant for the script and style directives. It means you can use scripts and styles inline, both in the head or as an attribute of a tag. It is recommended not to use this as the only way to block inline scripts is to block them all. There is no mechanism to distinguish good or bad inline scripts. For Drupal, this can have serious consequences. More on that later.

'unsafe-eval'

This prohibits tricking of executable code in JavaScript based on (harmless) strings in your code. A few functions are prohibited with 'unsafe-eval': eval(), new Function(), setTimeout([string], …), and setInterval([string], …). This also has serious consequences for Drupal. Note that it’s not the use of the timing functions, it is about the fact that a string is used here as variable.

'Unsafe-inline', 'unsafe-eval' and Drupal

Drupal 7

When using 'unsafe-inline' in the script directive, without any precautions no javascript will function properly anymore. Drupal 7 stores all JavaScript settings as an inline (and thus executable) javascript itself. JS behaviour will be unpredictable. There is an issue for Drupal 7 to turn these settings into an inline JSON object, just as it is done with Drupal 8.

Apart from core, be sure that all JS is loaded as a file and not inline. Also make sure, you don’t use javascript in a href-attribute (e.g. href="https://dop.nu/blog/content-security-policy-and-drupal/javascript:…") or with an eventhandler attribute (f.i. onclick="…"). Both practices are highly discouraged anyway, but won’t work at all unless 'unsafe-inline' is added in the script directive.

It can be hard to get your site running properly without using 'unsafe-inline' but it can be done. Use the patch from the aforementioned issue, make sure all settings are loaded into and from the main JS Drupal settings, and place all scripts in a file instead of inline. When using drupal_add_js make sure to use the “file” parameter, instead of “inline” and place your javascript file in a separate module. Or use hook_js_alter to change this for javascript that is added by a contrib module.

Drupal 8

As mentioned, the javascript settings in Drupal 8 are stored as a JSON object. Besides this, all remarks for Drupal 7 are also relevant for 8. In Drupal 8 you use the asset library system for adding javascript (or js settings, or stylesheets) to your module or theme. Inline javascript is highly discouraged anyway. Drupal 8 is much more adapted to CSP than Drupal 7 already. Not surprisingly, CSP is more recently developed than Drupal 7.

Report-only

As mentioned earlier, not only the browser logs CSP violations, CSP itself does that too. In Drupal the standard configuration is to add those messages to the watchdog queue, but it is possible to write them to a custom file, see the seckit module configuration.

When setting up CSP for an existing site, there will probably already be content with external sources added to the site. How to find all these sources, and the directives they affect? Instead of adding sources to the directives, you switch on the “Report-only”-mode. Now, no CSP-headers will be rendered, but all violations will be logged. This is an excellent way to catch the external sources that need to be whitelisted. Once you put CSP in place, a non-whitelisted source will be blocked. A wrongfully blocked source could damage your site’s working or reputation.

A useful drush command to only show only CSP-violations while they come along is:

drush ws --type=seckit --tail --extended

More info

These sources were a welcoming help when searching for information:

Author: 
Original Post: 

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web