I have a Content-Security-Policy header in my .htaccess file that is good for an entire website with many pages. There are inline elements on each page that I do not (and cannot) move to separate .js files, and I want them to be allowed by the CSP as well WITHOUT using 'unsafe-inline'. Therefore, I'd like to add them at the top of each page using a dynamically generated PHP header() with the FALSE flag set. The FALSE flag (documented here) will append to any existing, incoming header without overwriting it. In theory, this should solve my problem...but it doesn't.
For reasons unknown, I cannot either append or replace the CSP set in the .htaccess file using a PHP header() before my HTML content. Below is code that demonstrates the problem: (1) the .htaccess file for the page; (2) the PHP page itself. You should be able to reproduce this with ease. (I'm using Chrome 57 for testing).
The sha256 hash is for the element as it appears below; change one character in it and the hash will no longer be valid for it. (If you are using certain browser extensions, Chrome may complain that additional hashes are needed; just add them to the PHP header(); conveniently, Chrome will provide you with the correct hash of each inline <script>
for the CSP )
If you rem-out the CSP directive in the .htaccess file, then the script executes. To prove further that it's working, change a character in the hash and reload the page with the console open; it should report an error saying that the CSP is not valid for the script, and then report what hash should be used for it to validate (which will be the original hash).
But if you then un-rem the CSP directive in the .htaccess file, the PHP header() has no effect, neither with the FALSE flag nor without it.
That is the problem: the PHP CSP header() has no effect when the .htaccess file has a CSP header.
I don't want to use a nonce
and I don't want to use unsafe-inline
or unsafe-eval
in the .htaccess file...and I don't want to add the hash to the .htaccess, as that's what I'm trying to bypass doing.
My guess is that there is an additional directive needed in the .htaccess file that will allow the PHP CSP header() to be activated. Bonus points for anyone who can both name that directive AND show a way to include the additional CSP not with a PHP header(), but with <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'sha256-6eM329mc/AyToTIZuNqhbTD9GNw5jUJeTFszXn+hRU8='">
, which I'd prefer to write directly to the HTML instead of using a PHP header() (but a solution either way would be fine).
Here is the relevant code to reproduce this problem:
.htaccess:
# The CSP here is pared down for simplicity of illustration
<IfModule mod_headers.c>
Header set Content-Security-Policy "script-src 'self';"
</IfModule>
PHP code:
<?php
header("Content-Security-Policy: default-src 'self'; script-src 'self' 'sha256-6eM329mc/AyToTIZuNqhbTD9GNw5jUJeTFszXn+hRU8='", FALSE);
?>
<!DOCTYPE html>
<html>
<head>
<title>Content-Security-Policy test</title>
</head>
<body>
<p>"z = 231" should appear in the console</p>
<script>var test=function(){var x = 10.5;var y = 22;var z = x * y;console.log('z = ' + z);}; test();</script>
</body>
</html>
By design, with CSP you can only increase the Content Security Policy, not decrease it.
So only define those policies you are (pretty sure) you don't want to change in htaccess.
For example:
<IfModule mod_headers.c>
Header set Content-Security-Policy "frame-ancestors 'self'; base-uri 'self'"
</IfModule>
Then set all the stuff you are likely to need to alter like script-src, connect-src, style-src etc in your 'page'.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With