Skip to content

Extension acts as fingerprinting beacon on strict CSP with report-uri sites #196

@ntninja

Description

@ntninja

Many websites nowadays use a security technology called Content Security Policy (CSP) to limit what attackers can do in case of compromise on the site. When deployed with strict settings this prevents any use of eval or <script> tags to execute JavaScript code on-the-fly: All of the page's scripts (and also CSS, etc) must be served from a set of trusted origins specified in the policy instead. This rule also applies to any scripts injected by extensions using window.eval or document.createElement("script").

The above would only render NanoDefender Pro ineffective on such sites, however CSP includes another feature that many sites use to monitor the issues caused by their CSP in the field called report-uri: Using this, a site can specify an arbitrary URL to be pinged whenever the browser detects a CSP violation. This again includes violations caused by extension scripts, like on the following example page (https://mm-csp-example.herokuapp.com/ was set up to test a related CSP issue with another extension):

{
  "csp-report": {
    "blocked-uri": "inline",
    "column-number": 33,
    "document-uri": "https://mm-csp-example.herokuapp.com/",
    "line-number": 214,
    "original-policy": "default-src 'self'; connect-src 'self'; font-src 'self'; frame-src 'self'; img-src 'self'; script-src 'self'; style-src 'self'; report-uri https://mm-csp-example.herokuapp.com/report_csp",
    "referrer": "",
    "source-file": "moz-extension://22f938b7-43d7-410d-91c6-80262d30dbd7/content/core.js",
    "violated-directive": "script-src"
  }
}

This can even include “samples” of the blocked content like in the following real-world example of undisclosed origin:

{
  "csp-report": {
    "blocked-uri": "inline",
    "column-number": 33,
    "document-uri": "",
    "line-number": 214,
    "original-policy": "default-src 'none'; connect-src 'self' …; font-src 'self' … data:; img-src 'self' … data: …; script-src 'report-sample' 'self' 'unsafe-eval' …; object-src …; manifest-src …; media-src …; style-src 'self' 'unsafe-inline'; frame-src …; worker-src …; report-uri …/csp",
    "referrer": "",
    "script-sample": "(() => {\n        try {\n            let _…",
    "source-file": "moz-extension://22f938b7-43d7-410d-91c6-80262d30dbd7/content/core.js",
    "violated-directive": "script-src"
  }
}

If that isn't a textbook example of a perfect fingerprinting vector based on the list of installed non-CSP-compliant extensions then I don't know. Even without the “script sample”, the combination of “just” the script name and line and column numbers is likely enough to unique identify every CSP violating extension in existence. And as I've discovered today, yours is by far not the only extension causing these kinds of reports to be generated.

So how to fix this? Well, the inject function and everything calling it will have to be rewritten in terms of the recommendations of the MDN article on Sharing objects with page scripts. In particular this means: Accessing page objects only through the .wrappedJSObject accessor (“X-Ray Vision”) and exporting objects from the content-script to the page scope using exportFunction/cloneInto only. Unfortunately, this whole concept is completely foreign to Chromium-based browsers (where using window.eval and related techniques is indeed the right thing to do), so the whole code currently in place needs to be retained if you want to continue supporting non-Firefox browsers as well.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions