CSP and inline scripts getting blocked - express

Struggling with understanding CSP, getting
Refused to apply inline style because it violates the following Content Security Policy directive: "script-src 'self' https://example;"
Example, my domain is https://example.com, in my policy, I have script-src 'self' https://example; yet inline scripts from https://example/static/js/19.b56ecbe.chunk.js is getting blocked.
I would have thought that 'self' would allow the script to run, what am I getting wrong here?

#sideshowbarker already explained why you cannot do that but there's a little variant you can use if you want to allow "inline scripts" on your pages.
Just include nonce in all your script tags as well as in the CSP header itself.
Look here for more details: https://www.troyhunt.com/locking-down-your-website-scripts-with-csp-hashes-nonces-and-report-uri/

Related

I have set the rule for CSP but the browser gives error with different rule of CSP

I have set my CSP in meta tag as follows:
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline' cdn.jsdelivr.net unpkg.com accounts.google.com code.jquery.com 'sha256-lJMW30jo60M6VSFHAaxO+izJVqSNTP0VHJM8ChvH0mQ=';" />
But the browser give different CSP as reference of error:
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src *". Either the 'unsafe-inline' keyword, a hash ('sha256-lJMW30jo60M6VSFHAaxO+izJVqSNTP0VHJM8ChvH0mQ='), or a nonce ('nonce-...') is required to enable inline execution."
Why that can be happened? Clearly i dont put the CSP as script-src *

Safari Gives Error "Refused to connect to _____ because it does not appear in the connect-src directive of the Content Security Policy."

I have a online whiteboard that Safari users cannot connect to. They get the following from the console.
Refused to connect to wss://whiteboard.[MYDOMAIN].com/[MOREPATHSTUFF] because it does not appear in the connect-src directive of the Content Security Policy.
Only Safari does this. Chrome, FF, Edge, etc. work fine. I've looked over other SO related posts and it seems that Safari requires something like...
<meta http-equiv="Content-Security-Policy" content="
default-src * data: blob: ws: wss: gap://ready file://*;
style-src * 'unsafe-inline';
script-src * 'unsafe-inline' 'unsafe-eval';
connect-src * ws: wss:;">
I have no idea what all of this means though. All I want is for Safari to allow the connection and all should be well. Thanks for the consideration on how to make that happen.
I've looked over other SO related posts and it seems that Safari requires something like...
You already have a Content Security Policy (CSP) been published via an HTTP header or meta tag (how to check it), because a violation message appears in the browser console.
Publishing the second CSP cannot relax the resulting policy, since the first CSP will still perform a blocking.
That's why you couldn't fix the problem by adding a meta tag or HTTP header.
You have to figure out where the first CSP is published and to make changes to it.
Only Safari does this. Chrome, FF, Edge, etc. work fine.
Safari supports the connect-src directive, so its behaviour should not differ from Chrome/Firefox/Edge except one thing: Safari does not support upgrade ws: to wss:.
In case of connect-src ws:, the Chrome/Firefox/Edge will apply connect-src ws: wss: policy, but Safari does not.
I bet you have connect-src ws: in the CSP, therefore all connection to wss: are blocked.
When you find where your CSS is published, just add wss://whiteboard.[MYDOMAIN].com to connect-src directive.
Note: if you are using the default-src directive instead of connect-src - then you need to add wss://whiteboard.[MYDOMAIN].com into it.
Update:
How CSP header can be issued
A). A CSP header can be published in your web app itself (Helmet middleware, specific packages for managing HTTP headers, direct issue an HTTP header via res.setHeader() and so on).
B). CSP header can be published by server. For Nginx it should be add_header Content-Security-Policy "default-src ... "; in the server config. Several folders can be used for that but etc/nginx/conf.d is preferred, others are deprecated.
What if I publishes CSP header in both ways A) and B) at the same time?
Server works after your web app software did all job, therefore web-server (or proxy-server) should override an HTTP header with the same name. So server can override any HTTP header published by web app.
In some cases it's not work and you can see that 2 CSP headers been published. Even in this case you can make a trick:
proxy_hide_header Content-Security-Policy;
and then immediately under that, add header how fo you need it:
add_header Content-Security-Policy "default-src 'self';";
This can be used as a temporary solution until you figure out where the header is really published.
CSP has some unintuitive traits. One is:
Websockets like wss://example.com are not captured by wildcards such as *.example.com or even *
In order to allow the websockets - you need to explicitly add them with wss://example.com or even ws: wss:
If you only want to allow anything on the connect-src, then adding
connect-src * ws: wss:; to your policy should work (as you mentioned).

Generate a nonce with Apache 2.4 (for a Content Security Policy header)

We're working on creating a strict Content Security Policy (https://csp.withgoogle.com/docs/strict-csp.html) which necessitates Apache creating a nonce each time a resource is requested, so that we can insert this nonce into the http header.
How can we create a nonce with Apache 2.4?
All of the CSP related documentation I've read says something to the effect of "A nonce is just a random string that's generated on the server, included in the CSP header..." but haven't found any info on how to do this with Apache. We could of course do this with app code, but doing it via Apache seems like a cleaner solution/will ensure every single page gets the CSP header.
You need to generate the nonce on the server, and then have Apache pass that nonce to your script where it can be used.
We've created an open source module for Apache that simplifies this process: mod_cspnonce.
Here's a simple example of the server-side config:
LoadModule headers_module modules/mod_headers.so
LoadModule cspnonce_module modules/mod_cspnonce.so
# add the CSP_NONCE to the "default-src"
Header add Content-Security-Policy "default-src 'self' 'nonce-%{CSP_NONCE}e';"
Here's a simple example of using the nonce in your script:
<script nonce="<?= $_SERVER['CSP_NONCE'] ?>">
var inline = 1;
</script>
That example is php, but you can use any language.
I would have preferred to simply add this as a comment but my reputation <50 does not allow it so I'm posting this as an answer instead.
In response to:
1.) apache generates a random string via mod_unique_id
This is a "unique" value not a "random" value, so you might want to be careful with its use as a CSP nonce.
2.) we insert this into our CSP header (not sure how to do this actually)
<IfModule mod_headers.c>
<FilesMatch "\.(htm|html|php)$">
Content-Security-Policy: script-src 'strict-dynamic' 'nonce-%{UNIQUE_ID}e' 'unsafe-inline' ' https:;
</FilesMatch>
</IfModule>
I hope this helps.
After finding mod_unique_id, this was very easy (http://httpd.apache.org/docs/current/mod/mod_unique_id.html).
1.) Enable mod_unique_id. This is generally one line in httpd.conf:
LoadModule unique_id_module modules/mod_unique_id.so
2.) mod_unique_id generates a unique string (see user3526609's answer, this may or may not be random enough for you) and server variable UNIQUE_ID set equal to that random string, on each page request, which you can then inject into your CSP and any inline code you need to whitelist. If you happen to use Php, $_SERVER['UNIQUE_ID'];
The nonce has to be inserted into any element with inline CSS and/or Javascript handlers, so it has to be inserted at the application layer.
Parsing your output HTML and inserting nonces would defeat the whole purpose of CSP nonces -- the web server has no way of knowing if inline CSS/JS was supposed to be present, or if it was inserted by an attacker.
In Apache you must have module called mod_unique_id enabled. He generates a unique environment variable (UNIQUE_ID). However, its encoding has illegal characters for csp [A-Za-z0-9#-] instead of the usual base64 [A-Za-z0-9+/].
You should use base64 encoding to generate proper values. For example:
Header set X-Nonce "expr=%{base64:%{reqenv:UNIQUE_ID}}"
Then to generate complete CSP policy do:
Header set Content-Security-Policy "expr=default-src 'self'; script-src 'self' 'nonce-%{base64:%{reqenv:UNIQUE_ID}}'"
In PHP use:
echo $_SERVER['HTTP_X_NONCE'];
to extract nonce.

Content Security Policy not working with SHA256

I'm creating a Content Security Policy on RedHat 6 Apache 2.2 for some html files with JavaScript links to Google. I have added the following code the virtualhost of the site in httpd.conf file.
Header always set Content-Security-Policy-Report-Only: "default-src 'self' https:; script-src 'self' https: https://www.google-analytics.com https://ajax.googleapis.com; style-src inline: 'self' https: 'sha256-j0bVhc2Wj58RJgvcJPevapx5zlVLw6ns6eYzK/hcA04=' https://www.google-analytics.com https://ajax.googleapis.com; font-src 'self'; img-src 'self' https: data: https://www.google-analytics.com; form-action 'self'; frame-ancestors 'none'; connect-src 'self' https: https://www.google-analytics.com; child-src 'self'"
Cannot get the policy to work unless I use 'unsafe-inline' which I would prefer not to. Latest Chrome browser in developer mode says to add SHA256 hash which I did (see below). Even though I added the recommended SHA256 hash Chrome still asks me to add it????
Am I not handling SHA256 hash correctly? Any help will be appreciated.
From Chrome developers console.
Refused to apply inline style because it violates the following
Content Security Policy directive: "style-src inline: 'self' https:
'sha256-j0bVhc2Wj58RJgvcJPevapx5zlVLw6ns6eYzK/hcA04='
https://www.google-analytics.com https://ajax.googleapis.com". Either
the 'unsafe-inline' keyword, a hash
('sha256-j0bVhc2Wj58RJgvcJPevapx5zlVLw6ns6eYzK/hcA04='), or a nonce
('nonce-...') is required to enable inline execution.
EDIT: This is discussed in this bug:
https://bugs.chromium.org/p/chromium/issues/detail?id=546106
Not totally following what the bug says but it seems the spec only allows sha256 hashes for script and style tags and not for inline scripts and styles. It appears you are going to have to add a 'unsafe-hashed-attributes' attribute to your policy to make it work.
However not totally clear if that is just to fix the script part or also styles, nor if it has been released. It definitely won't have made it to iOS devices that also use WebKit so, for now, I would just allow unsafe-inline for styles.
ORIGINAL ANSWER (before above answer was added but leaving to avoid confusion as comments added below).
I see a few problem:
The error mention the Content Security Policy but your example config is the Report Only header. I've seen issues when using both Content-Security-Policy and Content-Security-Policy-Report-Only. They should be independent but are not. Might be worth trying without one to see if that's the problem.
Your style source includes inline: which is not valid syntax AFAIK
You include the general https: directive and then also specific https sites (https://www.google-analytics.com https://ajax.googleapis.com)

Content Security Policy [Error] Refused to load the script safari

I am behind with CSP, this morning all sites on one of my servers stopped working with safari with the following error:
[Error] Refused to load the script
'http://code.jquery.com/jquery-1.9.1.js' because it violates the
following Content Security Policy directive: "default-src 'self'".
Note that 'script-src' was not explicitly set, so 'default-src' is
used as a fallback.
How can I fix this server-wide without having to change each sites 1 by 1.
As mentionned I am a bit behind with CSP, as such I don't even know where to put the rules
For future reference, here's what I had done 'incorrectly'.
in the file /usr/local/apache/conf/includes/pre_main_global.conf
I placed a bunch of default headers to secure the server including:
Header set X-WebKit-CSP: "default-src 'self'"
Which caused Safari to refuse any script not hosted on localhost.
The confusion came because nobody found the problem before a week after the fact.