Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting if code is being run as a Chrome Extension

I am working with some code that needs to be run as a page, and if it's being run as a Chrome Extension, I want to be able to do additional things. What I'm using is:

<script>
if (chrome && chrome.extension) {
    // extension stuff
}
</script>

This seems like a good capability detection. Using user agent string causes me trouble because it's the same no matter the context (web page vs. extension).

Question: are there other more reliable techniques for detecting if a piece of code is running inside a Chrome extension?

Update: I'm wondering if there's something I can put into my manifest.json file that I can then read back. Note, that the extension I'm working on is not intended as a persistent thing that runs all the time, it's a content application that runs in a single window or browser tab and has no need to interact with other windows or tabs or anything else.

like image 945
artlung Avatar asked Sep 21 '11 22:09

artlung


3 Answers

So many complicated answers here, while you can easily detect whether you're running in a Chrome extension by checking for existence and non-emptiness of chrome.runtime.id:

if (window.chrome && chrome.runtime && chrome.runtime.id) {
    // Code running in a Chrome extension (content script, background page, etc.)
}
like image 175
Rob W Avatar answered Oct 17 '22 21:10

Rob W


I had the need for something similar.

But I didn't need to care about sites attempting to trick the code.

const SCRIPT_TYPE = (() => {
    if (chrome && chrome.extension && chrome.extension.getBackgroundPage && chrome.extension.getBackgroundPage() === window) {
        return 'BACKGROUND';
    } else if (chrome && chrome.extension && chrome.extension.getBackgroundPage && chrome.extension.getBackgroundPage() !== window) {
        return 'POPUP';
    } else if (!chrome || !chrome.runtime || !chrome.runtime.onMessage) {
        return 'WEB';
    } else {
        return 'CONTENT';
    }
})();

The above should detect the 4 scenarios

  • javascript is run in a background page
  • javascript is run in a popup page / iframe
  • javascript is run in a context script
  • javascript is run in a directly on a website
like image 44
Chad Scira Avatar answered Oct 17 '22 23:10

Chad Scira


Pragmatically that's a good approach. Theoretically (not sure if this is relevant or not, e.g. may provide vulnerabilities) it can be spoofed very easily. I suppose it depends on your context how relevant that is.

Here's a slightly stronger idea:

if (chrome &&
    chrome.windows &&
    chrome.windows.get &&
    typeof chrome.windows.get === 'function' &&
    chrome.windows.get.toString() === 'function get() { [native code] }')

The idea is the same as yours, although it's slightly stronger, since AFAIK having an object be a function and having it's toString() value have that value is impossible since it's not valid syntax, so even trying to spoof that value wouldn't work unless you altered the native code (which requires a whole different level of hacker).

Don't offhand remember if checking things like this requires permissions or not, but the idea is clear I hope.

UPDATE

I just realised that the "native code" syntax idea can be fooled, by aliasing an existing function. E.g.

var FakeFn = Object.create;
FakeFn.toString(); // "function create() { [native code] }"

But that can be taken care of by careful selection of which function we use, since the name appears in the string. get is probably too common, but if we take an obscure function name (like captureVisibleTab of chrome.tabs.captureVisibleTab) that is implemented only in chrome extensions, it is still a very portable solution, because unlike the basic check where code can be fooled by other local user code, it is known in advance that the browsers don't implement any native functions with this name, so it's still safe in all browsers and with all user code.

UPDATE

As @Mathew pointed out, this idea is foolable (although seemingly only maliciously). I thought I could patch the problem by comparing to Function.prototype.toString but figured that even that can be fooled by aliasing the original toString method and creating a new one that for certain functions returns false strings and for others returns the original string.

In conclusion, my idea is slightly stronger than the original, in that it will rule out practically all chance of unintentional collision (slightly more than the OP's idea), but is certainly no defence against a malicious attack as I first thought it might be.

like image 44
davin Avatar answered Oct 17 '22 23:10

davin