privacybadger icon indicating copy to clipboard operation
privacybadger copied to clipboard

PB in Firefox causes CSP violation on sites that disallow script-src = self

Open csmith opened this issue 7 years ago • 36 comments
trafficstars

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.

csmith avatar Nov 28 '17 03:11 csmith

I believe this is the relevant Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1267027.

ghostwords avatar Nov 28 '17 21:11 ghostwords

Could you provide an example page where this happens?

ghostwords avatar Nov 28 '17 22:11 ghostwords

For example on https://web.threema.ch/ (no need to log in, just open the debug console and load the page).

dbrgn avatar Dec 05 '17 09:12 dbrgn

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.

ghost avatar Dec 13 '17 02:12 ghost

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.

SeinopSys avatar Feb 22 '18 17:02 SeinopSys

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.

ghostwords avatar Feb 22 '18 19:02 ghostwords

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!

IBBoard avatar Mar 02 '18 16:03 IBBoard

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.)

ghostwords avatar Mar 02 '18 18:03 ghostwords

I confirm this.. I ended up here after seeing strange CSP reports about that V8_STACK script..

To recreate this issue:

  1. Install the Privacy Badger extension
  2. Open the Inspector (Ctrl + Shift + I)
  3. Visit any site that has CSP enabled for script-src without the 'unsafe-inline' (recreated this on https://web.threema.ch/ as of today).

You will see this:

image

Earlier those 2 violations did not happen. Installing Privacy Badger than caused those to happen.

kaushalmodi avatar Apr 30 '18 14:04 kaushalmodi

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 :)

kaushalmodi avatar Apr 30 '18 14:04 kaushalmodi

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.

ghostwords avatar Apr 30 '18 14:04 ghostwords

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!

kaushalmodi avatar Apr 30 '18 15:04 kaushalmodi

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.

ghostwords avatar Apr 30 '18 15:04 ghostwords

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

kaushalmodi avatar Apr 30 '18 19:04 kaushalmodi

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.


Details

Update: Updated instructions using the base64 sha256 calculation code from below.

kaushalmodi avatar Apr 30 '18 20:04 kaushalmodi

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));
  });
}

ghostwords avatar Apr 30 '18 21:04 ghostwords

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.

kaushalmodi avatar May 01 '18 01:05 kaushalmodi

Would there be any way to avod those CSP violations in the first place?

dbrgn avatar May 01 '18 06:05 dbrgn

@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.

ghostwords avatar May 01 '18 14:05 ghostwords

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:

image

(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:

image

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):

image

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.

kaushalmodi avatar May 01 '18 14:05 kaushalmodi

@ghostwords thanks for the links :slightly_smiling_face:

dbrgn avatar May 01 '18 14:05 dbrgn

@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.

ghostwords avatar May 01 '18 14:05 ghostwords

This might be a Firefox-only issue. Could somebody confirm that CSPs do not apply to extension content script-injected inline scripts in Chrome?

ghostwords avatar May 01 '18 14:05 ghostwords

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!

IBBoard avatar May 01 '18 18:05 IBBoard

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.

kaushalmodi avatar May 01 '18 18:05 kaushalmodi

Could privacy badger not inject its own script hashes into the CSP header to solve this?

lightswitch05 avatar Jun 27 '18 19:06 lightswitch05

That's a possible approach, I think. By the way, is this a Firefox-specific issue, or is Chrome similarly affected?

ghostwords avatar Jun 27 '18 20:06 ghostwords

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

lightswitch05 avatar Jun 27 '18 21:06 lightswitch05

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?

lightswitch05 avatar Jun 27 '18 21:06 lightswitch05

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.

ghostwords avatar Jun 27 '18 21:06 ghostwords