Why is my Content Security Policy not being adhered to? - google-chrome

I've come a cross a very bizarre situation where a hash is being ignored, despite it being present in the Content-Security-Policy.
This happened while installing Hotjar on our website, manually adding hashes for every inline script it uses, but obviously it can presumably happen with any dynamically inserted inline script.
In the console error message below you can see that the required hash is present, but Chrome suggests that it needs to be added...
Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'nonce-llX8ZkdD3suoiNrpE9mCatplNhRYmlKw' 'self' 'sha256-HRecKxp1fRukFUlrmQh3cAVyb/pNYtdWFGJ2EL5FzdE=' 'sha256-SvLgADqEePEV9RNxBrRQXSBJafFHcVNG7cPzHz6h9eA=' 'sha256-fGP7dUodgG1o2qqo7hPGqd+2FEE7z2Z4Xg5muj+XIOQ=' 'sha256-8hoDThJonkR/uDTFl5y8ugf9U3kcHPL2sq19iPFHTds=' 'sha256-ecMh1s2mivgxX0zzJbkamgAS7kPx+1EqcHz8Uz30i78=' 'sha256-Qv05/NsT/MWFR5NB3hDHRW9iI424uc8WpuRssGdOAsU=' 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' 'sha256-S3EaMUkdpUGJFgSHH5d/29s3oD8/sutxvMfQoprfQ+g=' 'sha256-qVlOiWrAwuIfu8+uHKHkgg4qBA7YOoSm8A0yB4LfrNw=' *.hotjar.com *.typekit.net".
But the remedy is to include a hash that's already present:
Either the 'unsafe-inline' keyword, a hash ('sha256-S3EaMUkdpUGJFgSHH5d/29s3oD8/sutxvMfQoprfQ+g='), or a nonce ('nonce-...') is required to enable inline execution.
The hashes in CSP and proposed solution are identical:
And here is the complete CSP:
Content-Security-Policy: default-src *; base-uri 'self'; img-src * data:; style-src 'nonce-{$nonce}' 'self' 'sha256-HRecKxp1fRukFUlrmQh3cAVyb/pNYtdWFGJ2EL5FzdE=' 'sha256-SvLgADqEePEV9RNxBrRQXSBJafFHcVNG7cPzHz6h9eA=' 'sha256-fGP7dUodgG1o2qqo7hPGqd+2FEE7z2Z4Xg5muj+XIOQ=' 'sha256-8hoDThJonkR/uDTFl5y8ugf9U3kcHPL2sq19iPFHTds=' 'sha256-ecMh1s2mivgxX0zzJbkamgAS7kPx+1EqcHz8Uz30i78=' 'sha256-Qv05/NsT/MWFR5NB3hDHRW9iI424uc8WpuRssGdOAsU=' 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' 'sha256-S3EaMUkdpUGJFgSHH5d/29s3oD8/sutxvMfQoprfQ+g=' 'sha256-qVlOiWrAwuIfu8+uHKHkgg4qBA7YOoSm8A0yB4LfrNw=' *.hotjar.com *.typekit.net; script-src 'nonce-{$nonce}' 'self' 'sha256-A0/707MQdpfr/tR18VnYSk7JMJoUQSBURZEJa8wF6po=' 'sha256-1kpOd8fXCkigqXNekDPt+noalDB6YI+94YhtU3ETmvE=' *.hotjar.com *.googletagmanager.com *.universe.com *.google-analytics.com *.quantserve.com *.quantcount.com *.ads-twitter.com *.facebook.net analytics.twitter.com *.stripe.com polyfill.io *.queue-it.net *.amplitude.com; object-src 'none'; frame-ancestors 'self' *.queue-it.net
To replicate this all you'd have to do is install Hotjar with the above CSP.
Is this a Chrome bug or have I missed something?
Here's a screenshot for anyone interested (click to zoom in).

This issue is the same as CSP header fails with "Refused to apply inline style..." but I have already added the hash.
You have either an:
inline event handler in the tag like onclick='javascript_here', onload='js_handler()' etc.
OR
javascript-navigation like <a href='javascript:...'
Chrome calculates hashes for those but to allow this kind of inline script you need to also add unsafe-hashes token to the 'script-src'.
Note: Safari 12 does not support 'unsafe-hashes', therefore may be better to hang event handlers with addEventListener() in case of 1.

Related

Unexpected blocked page when called from link in iframe to PDF file with chrome based browsers

I have a webpage in an iframe which contains a link to a foreign page with attributes target="_blank" rel="noopener". This link works well from the page itself and also from the iframe but only when called via context menu with the option open in new tab. A regular click opens a new tab and shows the correct URL but results in This page has been blocked by Opera ERR_BLOCKED_BY_CLIENT.
I looked around for help, disabled all addons to no avail and played the scenario in incognito mode as well, no change. The console on the error page shows VM589:1460 crbug/1173575, non-JS module files deprecated. This hint left me in the dark, however. I manipulated the attributes to target="_blank" rel="noopener" rel="noreferrer" or rel="noopener noreferrer" with no success. This all applies to Opera.
I tried Brave with the same result and error message, except Brave instead of Opera, of course. Edge joins the failure band. No surprise then to see This page has been blocked by Chrome proper as well.
This error applies to all PDF-file links from the iframe, normal external links work well. Firefox shows briefly PDF.js viewer in the new tab and then works normal. I had no chance to try links to YouTube or the like, but I guess there is ample evidence I hit a bug in the chrome PDF-rendering engine.
What to do? Who is interested in this bug and how to reach them? Or is there anything I can do in my code?
Browser use already longer time the Content Security Policy (CSP) to protect users from undesired content. The definition of undesired content can be made by the site owner. if the site owner hasn't done that, then some standard values are used, which are more restrictive.
The website https://content-security-policy.com/ explains it like this
What is Content-Security-Policy?
Content-Security-Policy is the name of a HTTP response header that modern browsers use to enhance the security of the document (or web page). The Content-Security-Policy header allows you to restrict how resources such as JavaScript, CSS, or pretty much anything that the browser loads.
Although it is primarily used as a HTTP response header, you can also apply it via a meta tag.
The term Content Security Policy is often abbreviated as CSP.
What types of attacks does Content-Security-Policy help mitigate?
CSP was first designed to reduce the attack surface of Cross Site Scripting (XSS) attacks, later versions of the spec also protect against other forms of attack such as Click Jacking.
In your case you have to configure two hurdles for the user:
The iframe, required rule:
frame-src 'self'
The PDF, required rule:
object-src 'self' blob;
Essentially it's relatively safe to allow most things with 'self' but it's always good to disallow elements that are not used at all on the site.
Your Content Security Policy should look similar to this, and should allow PDFs in iframes:
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; object-src 'self' blob:; style-src 'self'; frame-src 'self';
The best is to configure the CSP in the server configuration. This is not possible for every site owner though, and it's possible to configure the CSP in meta tags too. You could add this to your site inside the head-tag:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; object-src 'self' blob:; style-src 'self'; frame-src 'self'">
Setting the CSP in the server configuration is the stronger approach though:
https://content-security-policy.com/examples/meta/
Here are more examples, including CSP related server configuration for Apache Web Server and Nginx Webserver:
https://content-security-policy.com/examples/
EDIT
If the PDF is hosted on another domain, then the CSP has to adjusted differently, more openly.
Different options for sources are explained here:
https://content-security-policy.com/#source_list
The iframe, required rule has to be adjusted.
It should be restricted either to a special domain with *.example.com or to a nonce with 'nonce-rAnd0m'.
The PDF, required rule has to be adjusted, the same advise like for point 1. is applying.
So even the PDF is external then, it doesn't mean that everything could be injected somehow in the website.
Assumed you use the option with the domain *.example.com it would look like or similar to this then:
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; object-src '*.example.com' blob:; style-src 'self'; frame-src '*.example.com'
Assumed you use the option with the nonce it would look like or similar to this then:
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; object-src 'nonce-rAnd0m' blob:; style-src 'self'; frame-src 'nonce-rAnd0m'
I didn't test it, so slight adjustments might be required.

GTM nonce-aware script does not work on all browsers for Custom HTML tags

I have a Vue application that uses SSR. In order to implement Content-Security-Policy, I use nonce-aware version of GTM snippet.
But it was not adding the nonce to all scripts that get injected by gtm to the page (specifically Custom HTML tags). I followed the solution mentioned here and it fixed the issue on Safari. But it doesn't work on Chrome, Firefox, or Edge and I still see the error for those tags:
this is my CSP settings:
default-src 'self';
base-uri 'self';
block-all-mixed-content;
font-src 'self' https: https://fonts.gstatic.com data:;
img-src 'self' https: data: https://www.google-analytics.com https://ssl.gstatic.com https://www.gstatic.com https://www.googletagmanager.com;
object-src 'none';
script-src 'self' 'nonce-b62382357618aee340fc9dc596c94a19' https://www.google-analytics.com/ https://ssl.google-analytics.com https://tagmanager.google.com https://www.googletagmanager.com 127.0.0.1:*;
script-src-attr 'none';
style-src 'self' 'unsafe-inline' https://tagmanager.google.com https://fonts.googleapis.com;
upgrade-insecure-requests;
connect-src 'self' https://www.google-analytics.com https://stats.g.doubleclick.net;
After hours and hours of research I found the following comment:
Chrome masks the nonce attribute value so tag manager is unable to grab it and store it as a variable.
I am not sure if this is really the issue but can anyone please explain how I can tackle this issue without using unsafe-inline or hashes as it seems there is no standard way of fixing it even on Google documentations?
After searching a lot on the internet and reading different articles, I found out the solution mentioned here works perfectly well in Safari but not in other browsers because:
Chrome, Safari, and Edge mask the nonce attribute value so GTM is unable to grab it and store it as a variable.
Considering this solution for propagating the nonce to the custom HTML tags, we go ahead and apply the following changes to fix this issue on Chrome, Firefox, and Edge.
As suggested here in the comments we need to modify the nonce-aware script a little bit and put the nonce in a data- attribute (then the browsers won't mask it), and push it to the dataLayer:
<!-- Google Tag Manager -->
<script
nonce='{{YOUR_GTM_NONCE}}'
data-gtm-nonce='{{YOUR_GTM_NONCE}}'
>
(function(w,d,s,l,i){
const gtmNonce = d.querySelector('[data-gtm-nonce]').dataset.gtmNonce;
w[l]=w[l]||[];
w[l].push({'nonce': gtmNonce});
w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});
const f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';
j.async=true;
j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;
j.setAttribute('nonce', gtmNonce);
f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','YOUR_GTM_ID');
</script>
<!-- End Google Tag Manager -->
Then in the GTM, you need to change the nonce variable you defined based on this solution from this:
to this:
The rest will be kept the same.

How do you find which script is causing the CSP error?

Google started throwing this message out of the blue:
"Content Security Policy of your site blocks the use of 'eval' in JavaScript"
I had no policy config'd, so to test, I tried setting my CSP to (both in the HTML and in the web.config):
<add name="Content-Security-Policy" value="default-src *; style-src 'self' 'unsafe-inline';
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://js.squareup.com/; report-uri /csp_report_parser;" />
I get nothing in the report and google won't tell me the source:
How do I find the offending code?
Thanks!
Add 'report-sample' token to the script-src directive. It should send samples of code caused the violation - 40 chars, also line number will be send (as I can see you do use violation reports feature).
In the browser console you could see the function names (green arrow) and line number (blue arrow at the printscreen below) where the violation occurred.
The results of report-sample underlined in red:
You can catch SecurityPolicyViolation event by javascript - all what going to be sent in the violation report is accessible, include line/column number.
Google started throwing this message out of the blue:
It could not be out of blue. May be the default CSP rules was activated after software upgrade or you touch the settings.
Anyway you CSP script-src 'self' 'unsafe-inline' 'unsafe-eval' allows eval-expressions, so you have somewhere another CSP issued.

how to set multiple Content Security Policies in a chrome extension manifest.json file

in the Google chrome documentation I found that I can add content Security Policy to allow an external javascript file to work on my extension.
but I couldn't find how to add multiple ones. Is it an array of Strings?
"content_security_policy": "script-src 'self' https://example.com; object-src 'self'"
I tried to put multiple lines like that but it doesn't work. Goes error:
Refused to load the script https://example.com because it violates the following Content Security Policy directive: "script-src 'self' https://example.com". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
CSP policy is a single string (containing a semicolon-separated list of directives and their arguments). It applies to all extension pages.
If you need a single policy with multiple sources, you can do that. In fact, you already have that: 'self' and https://example.com are two sources.
Read about CSP in general and script-src directive, e.g. on the MDN.
Syntax
One or more sources can be allowed for the script-src policy:
Content-Security-Policy: script-src <source>;
Content-Security-Policy: script-src <source> <source>;
So you just need to space-separate them between script-src and the semicolon.
Make sure that your sources do not contain paths.
E.g. https://example.com is OK, but https://example.com/ or https://example.com/script.js are not.
If you need multiple independent policies for different pages, I'm afraid you can't do that.

Content-Security-Policy object-src blob

When using a content-security-policy and I try to follow a process in Chrome 41 (beta) using window.URL.createObjectURL I get an error like the following:
Refused to load plugin data from 'blob:http%3A//localhost%3A7000/f59612b8-c760-43a4-98cd-fe2a44648393' because it violates the following Content Security Policy directive: "object-src blob://*"
With a content security policy that restricts object-src or otherwise default-src one can reproduce the issue (with jQuery for convenience) like this:
blob = new Blob(
["%PDF-1.\ntrailer<</Root<</Pages<</Kids[<</MediaBox[0 0 3 3]>>]>>>>>>"],
{ type: "application/pdf" })
$("<embed>").attr("src", window.URL.createObjectURL(blob))
.appendTo(document.body)
It seems from the spec that this should work, as it does for data://*. I have tried also blob, blob:, blob:*, blob:http*, blob:http:*, blob:http://*, but to no avail.
What does work, but for apparent reasons is undesirable, is object-src *.
Has anyone had any success getting blobs to load with a content security policy? Is this a problem upstream, or have I overlooked something?
The spec compliant answer is object-src 'self' blob:
blob: should only match blob: explicitly, and not 'self' or *. This is a bug in Chrome, and was recently fixed in Firefox 40.