Chrome Extension Detection
Websites can detect the presence of Chrome extensions in a user's browser by sending specific URL requests that use the extension’s fixed ID and attempt to access internal extension resources exposed to the web, known as web-accessible resources. This method can be used to fingerprint the browser, reveal aspects of user's identity or interests, and in some cases, expose them other attacks such as XSS through vulnerable extensions.
This demo tries to detect installed extensions by scanning your browser for the 5,000 most popular extensions affected by this issue.
Chrome Extensions Fingerprint | ||
Extensions Hash | ||
Web Accessible Resources Detection | ||
Extensions |
Detected 0 of 5000 Extensions
|
How Chrome Extension Detection Works
Chrome extensions can be detected by attempting to access their internal resources (such as images, scripts, or HTML files) from a web page. If the resource is accessible, it indicates that the extension is installed and active in the user's browser (extensions that are disabled cannot be detected).
To see if a user has Google Translate active, an attacker could try to request it using the Fetch API:
- // The URL contains the extension’s ID and one of its internal resources:
- fetch("chrome-extension://aapbdbdomjkkjkaonfhkkikfgjllcleb/popup_css_compiled.css")
- .then(() => {
- console.log("Google Translate detected");
- })
- .catch(() => {
- console.log("Google Translate not detected");
- });
However, not every internal extension file is accessible from the web. Only those declared under the "web_accessible_resources"
property in the extension’s manifest file can be requested this way.
Additionally, in the latest Google Chrome versions, the "use_dynamic_url"
element has started working, protecting extensions from fingerprint by generating a new ID for each session. That means the URL is regenerated when the browser restarts or the extension reloads.
Below is an example snippet from the Google Translate manifest.json:
- "web_accessible_resources": [{
- "resources": ["popup_css_compiled.css"],
- "matches": ["<all_urls>"],
- // "use_dynamic_url": true
- }],
If the last line with "use_dynamic_url"
had been enabled, the resource would not have been fetched. If all resources use dynamic URLs, the extension cannot be detected. Unfortunately, today very few extensions that use web-accessible resources also enable this feature.
Timing Attack for Web Accessible Resources
Some extensions have implemented protection against requesting web-accessible resources from the web, by generating a secret token on each request. This protects against reading and including resources on the web page, but doesn't quite protect against detection.
It is possible to measure how long a request takes, and based on this, infer whether the extension is enabled or not. Fetching an enabled extension will, in most cases, take slightly longer than fetching a non-existent, uninstalled, or disabled one. We receive an error and cannot read the file, but the extra delay indicates that the resource exists.
- chrome-extension://fakeiddddddddddddddddddddddddddd/web_accessible_resources/noop.txt
- fake extension: we made 100 requests, average request time: 0.33ms
- chrome-extension://cjpalhdlnbpafiamejdnhcphjbkeiagm/web_accessible_resources/noop.txt
- uBlock Origin : we made 100 requests, average request time: 0.48ms
- 82/100 of requests to "cjpalhdlnbpafiamejdnhcphjbkeiagm" were slower, means we have uBlock enabled
This is an internal issue with Chrome, and this behavior cannot be fixed by add-on developers.
It is important to note that Brave is the only Chromium-based web browser that is not affected by this.
Further Reading
- Manifest V3 - Web Accessible Resources – Chrome Developers
- Manifest V2 - Web Accessible Resources – Chrome Developers
- Chrome extension source viewer (CRX Viewer) – GitHub