Thursday, April 4, 2013

The Evolution of Chrome Extensions Detection

Today's blog post is by guest blogger Giovanni Cattani.

The Old Technique


Detecting which Chrome extensions are installed has always been a trivial matter of finding a specific extension ID and trying to load the manifest.json file, which is always located in the same spot:

chrome-extension://abcdefghijklmnopqrstuvwxyz012345/manifest.json

However, in the latest Chrome versions, attempting to load the manifest usually fails and the JavaScript console returns the following error:

Denying load of chrome-extension://abcdefghijklmnopqrstuvwxyz012345/manifest.json. Resources must be listed in the web_accessible_resources manifest key in order to be loaded by pages outside the extension.

Load Error





What happened to the good old detection code that used to work seamlessly? And what is the web_accessible_resources manifest key?

Manifest Version 2

In Chrome 18, manifest version 1 was deprecated. Google is slowly decreasing its support, finally making it unavailable to developers in September 2013. In the new version, Google introduced several changes to the manifest file and one of them is breaking most of the existing detection code.

The web_accessible_resources array was introduced to allow developers to choose exactly which resources can be reached by external sources. If a local resource isn't included in the array, then a remote web page won't be able to load it. The array looks like the following example:

"web_accessible_resources": [ "logo.png", "menu.html", "style.css" ]

In order to determine the manifest version, the manifest.json file must be checked properly. The "manifest_version" parameter is self-explanatory and determines if the JSON has been updated to the latest version. If the manifest is already in the latest version ("manifest_version": 2), then the file includes the web_accessible_resources array.

Detecting Extensions

First of all, check the web_accessible_resources array and determine the resources that can be loaded. If the array is not empty, choose a resource of your choice, knowing that its location is chrome-extension://[EXTENSION ID]/[RESOURCE PATH].

Exactly like the old implementation, the simpler way to reliably detect whether an extension is installed or not with JavaScript is by creating a script element in the page and setting its source as one of the local extensions' resources. The following piece of code allows users to determine if a specific extension is installed by executing an arbitrary action when the local resource is loaded.

var testScript = document.createElement("script");           
testScript.setAttribute("onload", "alert('Extension Installed!')");
testScript.setAttribute("src", "chrome-extension://[EXTENSION ID]/[RESOURCE PATH]");
document.body.appendChild(testScript);


The previous code should work for every type of file, whether it's a text file (eg. json, html, script) or a picture.

Conclusion

The new implementation of the Chrome extension manifest provides developers with an easy way to whitelist resources loaded from external sources. Any code developed in order to detect installed add-ons won't be affected, as long as extensions keep allowing at least one local resource in the web_accessible_resources array.

-----

Giovanni Cattani works as a security specialist and likes to mess around with applications of all kinds. He occasionally contributes to BeEF and writes about stuff he likes at gcattani.co.vu

2 comments:

  1. Chrome performance with or without extensions has been really good and working with my documents as well as reading on the chromebook has been easy. Personally, I use a chrome extension for GroupDocs Viewer to view my files and read old PDF e-books. It has proved to be a handy extension for me and I get nice rendered files to view. You can also read an article for developing such extensions and I guess it will be a valued resource for developers as well as users.

    ReplyDelete
  2. "testScript.setAttribute("src", "chrome-extension://[EXTENSION ID]/[RESOURCE PATH]");"

    Here what to specify in [RESOURCE PATH]

    ReplyDelete