Working with Multiple Content-Security-Policy Headers

Working with Multiple CSP Headers

Today I’ve been fighting with Content Security Policy (CSP). Servers may send multiple CSP headers, but there is a catch:

Adding additional policies can only further restrict the capabilities of the protected resource

I had wrongly assumed that I could pretty up my nginx configuration by splitting up the various *-src directives into separate add_header lines.

This doesn’t work because subsequent Content-Security-Policy headers may only get more restrictive. So separate header with a sane default-src 'self' is already pretty restrictive and will override any other script-src directives in later (or earlier) headers that are more lenient. Combining everything into a single Content-Security-Policy header works just fine, however.

In other words, multiple Content-Security-Policy headers do not combine together. The most restrictive header is favored. Always. I had assumed they would combine at the directive level, but that’s not the case.

Non-Working Example

default-src is restrictive and connect-src allows wider permissions, so only default-src is used.

add_header Content-Security-Policy "default-src 'self'";
add_header Content-Security-Policy "connect-src 'self'";

Working Example

This one works fine because it’s a single CSP vs multiple.

add_header Content-Security-Policy "default-src 'self'; connect-src 'self'"

Making Nginx Configuration Pretty

I know the above examples seem trivial, but I was dealing with some very long lines for a CSP and ended up using this solution to split the lines. The sha384- bit is to allow Google Tag Manager’s initial script tag to load.

set $CSP "img-src 'self' data: https://* https://*";
set $CSP "${CSP}; script-src 'self' https://* https://* https://* 'sha384-Wa9NRoS9yNu6b38fmmt8wHM3n51XMbPXxQq88mKgWRi4NZQOeHW1ujxh7c+gdu5+'";
set $CSP "${CSP}; script-src-elem 'self' https://* https://* https://* 'sha384-Wa9NRoS9yNu6b38fmmt8wHM3n51XMbPXxQq88mKgWRi4NZQOeHW1ujxh7c+gdu5+'";
set $CSP "${CSP}; style-src 'self' 'unsafe-inline'";
set $CSP "${CSP}; style-src-elem 'self'";
set $CSP "${CSP}; font-src 'self'";
set $CSP "${CSP}; connect-src 'self' http://** https://* https://*";
set $CSP "${CSP}; frame-src 'self'";
set $CSP "${CSP}; default-src 'self'";
add_header Content-Security-Policy $CSP;