privacybadger
privacybadger copied to clipboard
PB in Firefox causes CSP violation on sites that disallow script-src = self
Visiting any site with a Content Security Policy that forbids 'self' scripts results in the following error logged to the console:
Content Security Policy: The page's settings blocked the loading of a resource at self ("script-src 'none'"). Source: (function (ERROR) { const V8_STACK_....
Which I believe is fingerprinting.js.
Presumably if a website also has CSP reporting enabled the browser will send a CSP report whenever a Privacy Badger user visits the site, which seems unideal all round.
I believe this is the relevant Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1267027.
Could you provide an example page where this happens?
For example on https://web.threema.ch/ (no need to log in, just open the debug console and load the page).
Visiting any site with a Content Security Policy that forbids 'self' scripts
It's the other way around. It forbids "none" (what the error message is referring to) which is a valid value for src. So you'll actually see the "V8_STACK" error on most CSP sites.
I'm implementing CSP (running it in report-only mode for now) and I got a report like this:
{
"csp-report": {
"blocked-uri": "self",
"document-uri": "https://example.com/my/page",
"line-number": 1,
"original-policy": "default-src 'none'; script-src https://example.com; report-uri <snip>",
"script-sample": "(function (ERROR) {\n\n const V8_STACK_...",
"violated-directive": "script-src https://example.com"
}
}
I had no clue what was causing it until I found this issue. The report came from a Firefox user-agent.
Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1267027 Chrome bug: https://bugs.chromium.org/p/chromium/issues/detail?id=749236 uBlock Origin workaround: https://github.com/gorhill/uBlock/issues/2823#issuecomment-366261107
The Chrome bug and the uBlock workaround may both be regarding CSPs blocking request redirection to data URIs (rather than CSPs blocking inline scripts created by extensions), but these are related issues.
So, is this a browser bug or is this a PrivacyBadger bug? I'm just trying to enable CSP on my site and encountering this, which means that it must be happening on any page that I've previously visited that had enabled CSP!
Both? Browser bug: extension scripts should not be bound by page CSPs. Privacy Badger bug: can we do something better until browsers fix the problem? (It seems like uBlock did.)
I confirm this.. I ended up here after seeing strange CSP reports about that V8_STACK script..
To recreate this issue:
- Install the Privacy Badger extension
- Open the Inspector (Ctrl + Shift + I)
- Visit any site that has CSP enabled for
script-srcwithout the'unsafe-inline'(recreated this on https://web.threema.ch/ as of today).
You will see this:

Earlier those 2 violations did not happen. Installing Privacy Badger than caused those to happen.
Until this gets fixed somewhere, can the PB devs provide an sha256 of those 2 scripts? Or in other words, where can I see the source of those 2 inline scripts? Ctrl + U on a web page does not help as those scripts are inserted by some extension voodoo :)
One lives in fingerprinting.js, the other in dnt.js. We have more content scripts though.
You can hash the output of these source-generating functions.
For example:
function getPageScript() {
// code below is not a content script: no chrome.* APIs /////////////////////
// return a string
return "(" + function (NAVIGATOR, OBJECT) {
OBJECT.defineProperty(OBJECT.getPrototypeOf(NAVIGATOR), "doNotTrack", {
get: () => {
return "1";
}
});
// save locally to keep from getting overwritten by site code
} + "(window.navigator, Object));";
// code above is not a content script: no chrome.* APIs /////////////////////
}
function buf2hex(buffer) { // buffer is an ArrayBuffer
return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
}
function sha256(input, callback) {
return window.crypto.subtle.digest(
{ name: "SHA-256", },
new TextEncoder().encode(input)
).then(hashed => {
return callback(buf2hex(hashed));
});
}
sha256(getPageScript(), r=>console.log(r));
//90db28c107c7a5632b6d01bb02d4b34ae310836b7f60ac260d676a78a7e6e012
The hashes will of course change whenever we update our injected scripts.
Thank you. I came across that when searching, but can I have the exact injected script (will the associated white-space)?.. because the sha will change based on the spaces too.
return "(" + function (NAVIGATOR, OBJECT) {
I'm not a JS coder.. so it's not clear what part of that whole code becomes the injected script, and also how the spaces will be in the injected script.
Just as Ctrl + U in browser shows the source code, is there something in Firefox dev tools that shows the exact script injected by PB?
Thanks!
Check out the code I pasted above. You can copy and paste the function that generates the script (getPageScript() is one), and execute it (in a JS console, does not have to be Privacy Badger's console) to get the exact script contents. I then use JS APIs to compute the hash, but you can use whatever you like once you have the output.
Thank you. I now realize that you had provided me the full code to get the sha256. Though, that sha does not look like the ones that I have generated and seen working.
Example: ;undefined generates Zgc2pp+yOmAVogxvTHYBMFKubY0HKk2a0+0+8sX17WY=. You can generate that by pasting ;undefined in https://report-uri.com/home/hash/.
Notice that this hash uses all the characters (it is base64 encoded) .. not just hex characters. What code would I need to generate the same kind of sha using JS APIs?
An alternative would be if you can provide a way to print the exact inline script in the Console. The I just need to paste that in https://report-uri.com/home/hash/ and get the sha.
From https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src:
'<hash-algorithm>-<base64-value>' A sha256, sha384 or sha512 hash of scripts or styles. The use of this source consists of two portions separated by a dash: the encryption algorithm used to create the hash and the base64-encoded hash of the script or style. When generating the hash, don't include the
A follow up: I was able to get the verbatim script inserted by PB.
Code to be pasted in Firefox Scratchpad
function getPageScript() {
// code below is not a content script: no chrome.* APIs /////////////////////
// return a string
return "(" + function (NAVIGATOR, OBJECT) {
OBJECT.defineProperty(OBJECT.getPrototypeOf(NAVIGATOR), "doNotTrack", {
get: () => {
return "1";
}
});
// save locally to keep from getting overwritten by site code
} + "(window.navigator, Object));";
// code above is not a content script: no chrome.* APIs /////////////////////
}
console.log(getPageScript());
Output in Console
(function (NAVIGATOR, OBJECT) {
OBJECT.defineProperty(OBJECT.getPrototypeOf(NAVIGATOR), "doNotTrack", {
get: () => {
return "1";
}
});
// save locally to keep from getting overwritten by site code
}(window.navigator, Object));
Pasting that in https://report-uri.com/home/hash/ gives 'sha256-kNsowQfHpWMrbQG7AtSzSuMQg2t/YKwmDWdqeKfm4BI=' (version 2018.04.23).
And similarly, the sha256 for the const V8_STACK .. script is 'sha256-afSIEhWGAPlY458Q4YkhPLVnsVEBh/u5YNlpg6od8TU=' (version 2018.04.23).
I have verified that addition these sha's to my script-src on https://scripter.co have successfully stopped the CSP reports for those 2 scripts caused by Privacy Badger.
Update: Updated instructions using the base64 sha256 calculation code from below.
Cool, yeah, I pasted a hex-encoded hash. If you want a base64 encoded hash in JS, you have to tweak your SHA-256 function to something like the following:
function _arrayBufferToBase64( buffer ) {
var binary = '';
var bytes = new Uint8Array( buffer );
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return window.btoa( binary );
}
function sha256(input, callback) {
return window.crypto.subtle.digest(
{ name: "SHA-256", },
new TextEncoder().encode(input)
).then(hashed => {
return callback(_arrayBufferToBase64(hashed));
});
}
Thank you! That works great! I was wondering.. would it be possible to update your extension release flow to include the sha for each of such inline scripts as part of the README? Then users simply need to copy paste the sha's from the README to their CSP.
Would there be any way to avod those CSP violations in the first place?
@kaushalmodi Sure, I just wonder how often this comes up, and whether this thread is good enough.
@dbrgn Yes, once Firefox and Chrome fix this extensions bug.
Sure, I just wonder how often this comes up,
This comes up every time someone visits a site with strict CSP like mine, and has this extension installed.
I picked 2 arbitrary sites with strict CSP's to demo this. Open the Web Console in Firefox (Ctrl + Shift +K) and visit https://vincent.bernat.im/en and https://www.josephearl.co.uk/. You will see something like:

(That Source: ;undefined is because of my Lastpass extension.. no idea what that injected script means..)
Guess what.. even on this page itself, I get this:

So talking about "how often this comes up", the question is more of "how often people care to see the violated CSP's reported for their domain" :)
I ended up on this thread because day before yesterday, I started seeing these CSP violations on my CSP reports endpoint (report-uri.com):

Now I don't have any of those at least specific to Privacy Badger. I still have other reports on injected scripts by other extensions, which I need to tackle..
and whether this thread is good enough.
This thread is good for now.. until one or more of future updates starts breaking the sha calculated in this thread before Firefox/Chrome do anything about fixing this on their end.
If it's not too much trouble, having the hashes in the README would be a good gesture to help people who care about keeping their sites secure using CSP.
@ghostwords thanks for the links :slightly_smiling_face:
@kaushalmodi Sorry, what I meant by "how often this comes up" is how often would somebody come looking for Privacy Badger hashes to add them to their site CSPs.
This might be a Firefox-only issue. Could somebody confirm that CSPs do not apply to extension content script-injected inline scripts in Chrome?
How much would publishing the current hashes help in the long run? I want this issue to go away as much as the next person, but if the hash is just for this version of PB then surely that means that you need to list a number of legacy hashes to account for people who haven't upgraded their extension yet? That just seems like it is going to get messy!
That just seems like it is going to get messy!
Hmm.. I agree. I guess that we then just wait for the browsers to get fixed.
Could privacy badger not inject its own script hashes into the CSP header to solve this?
That's a possible approach, I think. By the way, is this a Firefox-specific issue, or is Chrome similarly affected?
I am not able to reproduce on Chrome, but I also wouldn't consider this a bug in Firefox. In this case, Firefox is doing exactly what I am telling it to do by disabling this inline JavaScript. I guess Chrome is able to determine the source of the inline JavaScript (extension vs. website) and automatically add an exception for it since the user has clearly decided to run extensions that they want to interact with websites
It would be awesome if @scotthelme would weigh on in the best way to resolve this issue. Clearly Privacy Badger is a major cause of CSPs being reported. Is injection into the CSP header a viable option, or is this just going to have to be solved on a per-site basis? Maybe there are other options?
I think Mozilla is making progress on https://bugzilla.mozilla.org/show_bug.cgi?id=1267027 although no idea how close they are to a fix.