0

I know this has been asked already for the general case of Chrome extension script injection. I'm asking for my specific Web3js use case to see if anyone has any better ideas such that I don't need to inject.


I'm trying to sign an Ethereum transaction from a Chrome extension. To do this I'm using Web3.js. However, I need access to the window variable to instantiate web3. Something like:

    if (window.ethereum) {
      // current web3 providers
      window.web3 = new Web3(window.ethereum);
      await window.ethereum.enable();
    } else if (window.web3) {
      // fallback for older web3 providers
      window.web3 = new Web3(window.web3.currentProvider);
    } else {
      // no web3 provider, user needs to install one in their browser
      window.alert('No injected web3 provider detected');
    }

Since the content_scripts of a Chrome extension can't access the browser's window, I've had to inject both the web3js code, and my script using it, like so:

// inject script.js into the page.
var s = document.createElement('script');
s.src = chrome.runtime.getURL('js/script.js');
s.onload = function() {
    this.remove();
};
var w = document.createElement('script');
w.src = chrome.runtime.getURL('third-party/web3.min.js');
w.onload = function() {
    this.remove();
};
(document.head || document.documentElement).appendChild(w).appendChild(s);

This works fine. However, most major webpages have a CSP to block unsafe-inlines and unsafe-evals, both of which are occurring with this approach. So, my extension only works on web-pages without this CSP.

Can anyone think of a better way to achieve what I'm trying to achieve, or at least a way to get around the CSP?

Jackson Kelley
  • 412
  • 5
  • 13

1 Answers1

3

There are several solutions:

  • Strip/modify the CSP header of the site using chrome.webRequest.onHeadersReceived event, see this example (it's for a different header so you will change the name).

  • Modify the libraries so they don't use eval

  • Run the library in an iframe exposed via web_accessible_resources. That iframe will be an extension page like the popup/options page and you can apply your own CSP in manifest.json, more info.

    So, content script won't add third-party/web3.min.js, instead it'll add an iframe:

    var iframe = document.createElement('iframe');
    iframe.src = chrome.runtime.getURL('iframe.html');
    document.documentElement.appendChild(iframe);
    

    iframe.html:

    <script src="iframe.js"></script>
    <script src="third-party/web3.min.js"></script>
    

    Messaging will be somewhat complicated. Your page script (script.js) can use CustomEvent messaging to communicate with the content script which in turn can use chrome.runtime messaging to communicate with the iframe, example. Alternatively, use externally_connectable messaging directly so the page script (and the web page itself) can send messages to any extension script (the iframe script will also see it and decide if it should be handled by comparing sender.tab.id to its own tab id obtained via chrome.tabs.getCurrent).

wOxxOm
  • 65,848
  • 11
  • 132
  • 136
  • For stripping out the CSP header: based on a quick test it seems that not all websites are sending this header data. For example, if I try this approach on Twitter I don't see anything printed out when I print out all the headers. For other sites I'm able to do this and remove the CSP header. I don't think removing evals from the library is going to be feasible, but I suppose I can at least look into this or point out to the repo maintainers that this is blocking the library from being used in Chrome extensions. – Jackson Kelley Mar 18 '21 at 18:06
  • If I use the iframe approach the `window.alert('No injected web3 provider detected');` line is reached when initializing web3 (which is the same behavior I was seeing prior to injecting the scripts into the page directly). The problem is, the Ethereum wallet (i.e. "web3") is itself a Chrome extension which means it isn't accessible in the sandboxed environments, which means I *have* to inject the script in order to access the user's wallet in the window object. – Jackson Kelley Mar 19 '21 at 00:52