A Simple Content Security Policy

Its Easy

What is Content Security Policy

Basically CSP is a way to enforce a much stricter same origin policy. You can forbid all requests to external sites with the exception of form submits. By default CSP also forbids inline scripts and several forms of doing eval. Since this would make it unusable on the majority of existing sites there are options to allow that.

CSP currently only is implemented in Firefox 4, but this will be the major browser for a lot of sites pretty soon. Hopefully other browsers will implement the CSP spec, it is IMHO very much needed.

How do I Configure it

The easy part is to get apache to send an HTTP header to tell the browser your policy.

# Content Security Policy Header
Header set X-Content-Security-Policy-Report-Only \
  "allow *.your-domain1.tld *.your-domain2.tld; \
  options eval-script inline-script; \
  report-uri /perl/csp-report.pl"
  

The above enforces a strict same origin policy that allows only content from your-domain1.tld and your-domain2.tld while not restricting the execution of javascript. It only reports violations without actually enforcing the policy. It also disallows mixed schemas, e.g. http images in https pages. See bug 659015 (which is actually not a bug) for an explanation. To later enforce the policy it is sufficient to remove the -Report-Only.

All reports are sent in JSON format as POST bodies to /perl/csp-report.pl. Writing these to a log is actually the harder part. The first step is a perl script that sends a 204 No content response and moves the relevant data from the incoming JSON into outgoing headers.

#!/usr/bin/perl
#
# Report CSP violations by moving fields to response headers.
#

use warnings;
use strict;

use JSON;
use CGI;

use constant PREFIX => 'X-CSP-'; 

# Retrieve CSP data from the post body
my $json = CGI::param('POSTDATA');
my $data = decode_json($json);
my $csp = $data->{'csp-report'};

CGI::header(-status => '204 No Content');

foreach my $k (keys(%{$csp})) {
  next if ($k eq 'request-headers');
  CGI::header('-'.PREFIX.$k, $csp->{$k});
}

There is no harm in sending those data back and they can easily be logged with a custom apache log format.

# CSP Log
LogFormat "%h %l %u %t \"%{User-Agent}i\" \"%{Referer}i\"\
 \"%{X-CSP-request}o\" \"%{X-CSP-blocked-uri}o\"\
 \"%{X-CSP-violated-directive}o\"" csplog
SetEnvIf    Request_URI "^/perl/csp-report.pl" CSPLOG
CustomLog   /var/log/apache2/your-site-csp.log   csplog  env=CSPLOG

After reading this log for a while and probably adapting your site a little bit you can start enforcing your policy. You will most likely have a surprising lot of external JS URLs in the log. Don't be afraid, these are most likely requested by browser extensions.