Add Content Security Policy to WordPress [1/4]

Policy: a principle of behavior, wise conduct, a tool of governance, overall intention and direction as formally expressed by management.

By adding a Content Security Policy (CSP) to a blog, we can specify resources a reader’s browser (Chrome, Firefox, Edge) is allowed to use on a site. We can specify from where a browser can load scripts, styles, images, media, and objects. Specify to where a form can post data. Enable or disable inline scripts and styles; even exactly specify the fingerprint of allowed scripts and styles. Require that each file must have a specified fingerprint to make sure it was not modified.

You may have heard of a recent British Airways hack, or how thousands of UK and Australian governments’ sites were hijacked to mine crypto-currency. Both were compromised via a modified script file. Also a fresh news story about scripts injected into shopping carts. Attacking WordPress sites en masse is nothing new, and in most cases malicious scripts are injected into sites through some weakness, completely out of site owners’ control. Something should be done. Fortunately some smart people figured out how to remedy the situation, at least reduce the risk, so all we have to do is implement few recommendations.

To see how a CSP on a WordPress site looks, check out the source of this site. Right-click anywhere on the page and select “View Page Source” or type Ctrl+U. Take a look at the first <meta> tag, just below the <head>.

What does this do? The policy:

  • disables defaults for directives requiring sources (-src),
  • specifies two sha256- integrity attributes of inline scripts allowed to run,
  • allows loading of scripts from this domain and Cloudflare cdn,
  • specifies one sha256- integrity attribute of an allowed inline style,
  • allows loading of styles only from this domain,
  • allows loading of images only from this domain,
  • prevents loading of fonts,
  • prevents loading of objects,
  • allows forms to post only to this domain,
  • requires integrity attributes (sri) to be specified for styles and scripts.

Mozilla has a good reference of policy directives. This is likely to change as new specifications are introduced, but it is a good start. Once the policy is in place it is easy to tinker with.

Implementing CSP

What does it take to implement this? Well, WordPress is all about plugins: as for everything else, there are plugins for managing CSP and SRI (sub-resource integrity). The problem of managing CSP by a plugin is – that it is managed by a plugin. We have to trust the current version of the code, the current author, and the current owner of the plugin. As the plugin updates, we have to trust any future code, future authors, and future owners of that plugin. This is a bit too much of trust for something called “security policy”. After all, we are implementing the CSP because we are having problems due to placing too much trust in third parties.

My suggestion is to implement CSP manually, without use of plugins. However this requires some tinkering with WordPress theme and code. If you are comfortable with FTP, editing some code, and tinkering with theme files, you are good to go. If not, use plugins; you will still be better off by trusting relatively small number of plugin libraries, owners and authors than all the possible code libraries and all of their owners and authors.

The Roadmap

  1. Make a backup of the site, make sure to include all files and directories under the root domain.
  2. Create a child theme; all theme modifications will happen here. This allows the main theme to update without overwriting your changes. Also copy the header.php file from the main theme to the child theme.
  3. In wp-admin activate the child theme. If at any point things go wrong, you can always switch back to the main theme and continue later.
  4. Optionally, add few more security headers and a minimal CSP to the .htaccessfile. This influences both wp-admin and the site content. The CSP in the header.php influences only the site content; it is too restrictive for wp-admin.
  5. Study the site source and create your first CSP.
  6. Add the CSP to the header.php of the child theme. Prepare for stuff to break, use Chrome console (Ctrl+Shift+I) to monitor errors.
  7. Refresh the site in Chrome, note errors and keep fixing stuff. Keep going until all style and script sources are listed and there are no more errors.
  8. Clean up inline scripts. Remove the unsafe-inline from the script-src directive and note errors in chrome console. Write down all the sha256- strings in error messages add them to the script-src directive.
  9. Clean up inline styles. Ideally all styles should be defined in css files. All custom styling should be in file style.css of the child theme. Once done you can remove unsafe-inline from style-src directive. If few errors remain, add matching sha256- strings to the style-src.

This would be a good time to implement the step 1, the backup.

As a CSP is likely to change over time, here is my current policy, as can be seen in the source of this page, type (Ctrl+U).

default-src 'none';

script-src  
 'sha256-7y9/KNsyJQGWriyCQmEaf3FZwqU52r1AuCBxscB1YcY='
 'sha256-OetCjSROcXqSLk28/JijjIFbHKbc2k0pUrc6EinIPFg='
 'self' https://cdnjs.cloudflare.com ;

style-src
 'sha256-VQ+wVEjMeux0ClFzQ1fgfEzYFdPMJK1kR8kfwTPObo0='
 'self';

img-src     'self';
font-src    'none';
object-src  'none';
form-action 'self';
require-sri-for script style;

Next time: creating WordPress child theme, and adding security headers.