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' https://api.example.com";
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' https://api.exmaple.com"
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://*.google-analytics.com https://*.doubleclick.net"; set $CSP "${CSP}; script-src 'self' https://*.google-analytics.com https://*.googletagmanager.com https://*.google.com 'sha384-Wa9NRoS9yNu6b38fmmt8wHM3n51XMbPXxQq88mKgWRi4NZQOeHW1ujxh7c+gdu5+'"; set $CSP "${CSP}; script-src-elem 'self' https://*.google-analytics.com https://*.googletagmanager.com https://*.google.com 'sha384-Wa9NRoS9yNu6b38fmmt8wHM3n51XMbPXxQq88mKgWRi4NZQOeHW1ujxh7c+gdu5+'"; set $CSP "${CSP}; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com"; set $CSP "${CSP}; style-src-elem 'self' https://fonts.googleapis.com"; set $CSP "${CSP}; font-src 'self' https://fonts.gstatic.com"; set $CSP "${CSP}; connect-src 'self' http://*.MYWORK.com:* https://*.MYWORK.com https://*.google-analytics.com"; set $CSP "${CSP}; frame-src 'self' https://accounts.google.com"; set $CSP "${CSP}; default-src 'self'"; add_header Content-Security-Policy $CSP;