Summary:
All content injected into web content pages is currently subject to the
same Content Security Policy, regardless of who injected it. For
privileged callers, such as extension content scripts, this means that
some functionality can behave erratically, depending on the page they're
running on.
The plan here is to apply a separate CSP to content injected by certain
privileged callers, rather than subjecting it to page CSP. Content from
system URLs (like moz-extension:) is already immune to CSP. This change
will extend that immunity to any content injected by those callers.
Implementation plan:
Implementing this will require that we record the caller principal
whenever content that loads a URL is injected into a page, and use that
principal as the triggering principal for the load.
My plan is to add a special attribute to principals, specifying that
their CSP should override the CSP of document principals that they
subsume. Initially, this flag would only apply to expanded principals
used in extension content scripts.
The exact mechanics of capturing the triggering principal will need to
vary, but in general, it should happen any time a particular source
attribute is set. For instance, the following should all capture the
caller principal for the `src` URL at call time:
document.write(`<img src="
http://example.com/favicon.ico">`);
div.innerHTML = `<img src="
http://example.com/favicon.ico">`;
img.setAttribute("src", "
http://example.com/favicon.ico");
img.src = "
http://example.com/favicon.ico";
Other alterations to the node should retain the existing principals for
existing attributes. For example:
img.setAttribute("src", "
http://example.com/favicon.ico");
window.wrappedJSObject.img = img;
window.wrappedJSObject.eval("document.body.appendChild(img)");
should retain the principal from the original setAttribute call, even
though the last insertion happens with a content caller principal.
Scripts injected in this way will not gain any special immunity to CSP.
For instance, this call will succeed regardless of site CSP:
document.createElement("script");
script.setAttribute("src", "
https://cdn.framework.org/framework.js");
document.head.appendElement(script);
but any attempts by that script to inject content that violates CSP will
fail, as will any attempts to inject content via that framework
(e.g., `window.wrappedJSObject.$("head").append("<script>")`).
Security & privacy concerns:
This change will allow extensions to inject content into sites which can
(and probably will) cause security and privacy issues. However, it's
already quite easy for malicious or badly-implemented extensions to
create similar issues, and I don't think this change significantly
increases the risk. It may even mitigate it in some cases, since the
alternative of loading or evaling third-party scripts into the content
script sandbox would give them direct access to elevated privileges.
Per the CSP spec, those injections are assumed to be at the user's
behest, and should therefore take priority over the page author's
preferences.
Bug:
https://bugzil.la/1267027
Link to standard:
https://www.w3.org/TR/CSP/#extensions
Platform coverage: All platforms.
Estimated or target release: Firefox 58
Preference behind which this will be implemented:
No, it will be enabled by default.
Is this feature enabled by default in sandboxed iframes?
Yes.
DevTools bug: N/A
Do other browser engines implement this?
Chrome exempts content injected by extensions from CSP, though
it's somewhat inconsistent about how it handles its inline
scripts policy:
https://developer.chrome.com/extensions/contentSecurityPolicy#interactions
Edge appears to be planning to implement similar overrides, but
still subjects extension content to CSP:
https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11076023/
https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8074756/
Tests:
This functionality is not covered by web platform tests. There
will be new tests for its implementation in the WebExtension framework.