Welcome What's new in Chrome extensions Getting started API Reference Samples
Welcome What's new in Chrome extensions Getting started API Reference Samples

Stay secure

Published on Updated on

Extensions have access to special privileges within the browser, making them an appealing target for attackers. If an extension is compromised, every user of that extension becomes vulnerable to malicious and unwanted intrusion. Keep an extension secure and its users protected by incorporating these practices.

Protect developer accounts #

Extension code is uploaded and updated through Google accounts. If developers' accounts are compromised, an attacker could push malicious code directly to all users. Protect these accounts by creating specifically developer accounts and enabling two-factor authentication , preferably with a security key .

Keep groups selective #

If using group publishing, keep the group confined to trusted developers. Do not accept membership requests from unknown persons.

Never use HTTP, Ever #

When requesting or sending data, avoid an HTTP connection. Assume that any HTTP connections will have eavesdroppers or contain modifications. HTTPS should always be prefered, as it has built-in security circumventing most man-in-the-middle attacks.

Request minimal permissions #

The Chrome browser limits an extension's access to privileges that have been explicitly requested in the manifest. Extensions should minimize their permissions by only registering APIs and websites they depend on. Arbitrary code should be kept to a minimum.

Limiting an extensions privileges limits what a potential attacker can exploit.

Cross-origin XMLHttpRequest #

An extension can only use XMLHttpRequest to get resources from itself and from domains specified in the permissions.

{
"name": "Very Secure Extension",
"version": "1.0",
"description": "Example of a Secure Extension",
"permissions": [
"/*",
"https://*.google.com/"
],
"manifest_version": 2
}

This extension requests access to anything on developer.chrome.com and subdomains of Google by listing "/*" and "https://*google.com/" in the permissions. If the extension were compromised, it would still only have permission to interact with websites that meet the match pattern. The attacker would not be able to access "https://user_bank_info.com" or interact with "https://malicious_website.com".

Limit manifest fields #

Including unnecessary registrations in the manifest creates vulnerabilities and makes an extension more visible. Limit manifest fields to those the extension relies on and give specific field registration.

Externally connectable #

Use the externally_connectable field to declare which external extensions and web pages the extension will exchange information with. Restrict who the extension can externally connect with to trusted sources.

{
"name": "Super Safe Extension",
"externally_connectable": {
"ids": [
"iamafriendlyextensionhereisdatas"
],
"matches": [
"/*",
"https://*google.com/"
],
"accepts_tls_channel_id": false
},
...
}

Web-accessible resources #

Making resources accessible by the web, under the web_accessible_resources will make an extension detectable by websites and attackers.

{
...
"web_accessible_resources": [
"images/*.png",
"style/secure_extension.css",
"script/secure_extension.js"
],
...
}

The more web accessible resources available, the more avenues a potential attacker can exploit. Keep these files to a minimum.

Include an explicit content security policy #

Include a content security policy for the extension in the manifest to prevent cross-site scripting attacks. If the extension only loads resources from itself register the following:

{
"name": "Very Secure Extension",
"version": "1.0",
"description": "Example of a Secure Extension",
"content_security_policy": "default-src 'self'"
"manifest_version": 2
}

If the extension needs to include scripts from specific hosts, they can be included:

{
"name": "Very Secure Extension",
"version": "1.0",
"description": "Example of a Secure Extension",
"content_security_policy": "default-src 'self' https://extension.resource.com"
"manifest_version": 2
}

Avoid executable APIs #

APIs that execute code should be replaced with safer alternatives.

document.write() and innerHTML #

While it may be simpler to dynamically create HTML elements with document.write() and innerHTML, it leaves the extension, and web pages the extension depends on, open to attackers inserting malicious scripts. Instead, manually create DOM nodes and use innerText to insert dynamic content.

function constructDOM() {
let newTitle = document.createElement('h1');
newTitle.innerText = host;
document.appendChild(newTitle);
}

eval() #

Avoid using eval() whenever possible to prevent attacks, as eval() will execute any code passed into it, which may be malicious.

var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// WARNING! Might be evaluating an evil script!
var resp = eval("(" + xhr.responseText + ")");
...
}
}
xhr.send();

Instead, prefer safer, and faster, methods such as JSON.parse()

var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// JSON.parse does not evaluate the attacker's scripts.
var resp = JSON.parse(xhr.responseText);
}
}
xhr.send();

Use content scripts carefully #

While content scripts live in an isolated world, they are not immune from attacks:

  • Content scripts are the only part of an extension that interacts directly with the web page. Because of this, hostile web pages may manipulate parts of the DOM the content script depends on, or exploit surprising web standard behavior, such as named items.
  • To interact with DOM of web pages, content scripts need to execute in the same renderer process as the web page. This makes content scripts vulnerable to leaking data via side channel attacks (e.g., Spectre), and to being taken over by an attacker if a malicious web page compromises the renderer process.

Sensitive work should be performed in a dedicated process, such as the extension's background script. Avoid accidentally exposing extension privileges to content scripts:

  • Assume that messages from a content script might have been crafted by an attacker (e.g. validate and sanitize all input and protect your scripts from cross-site scripting).
  • Assume any data sent to the content script might leak to the web page. Do not send sensitive data (e.g. secrets from the extension, data from other web origins, browsing history) to content scripts.
  • Limit the scope of privileged actions that can be triggered by content scripts. Do not allow content scripts to trigger requests to arbitrary URLs or pass arbitrary arguments to extension APIs (e.g., do not allow passing arbitrary URLs to fetch or chrome.tabs.create API).

Register and sanitize inputs #

Safeguard an extension from malicious scripts by limiting listeners to only what the extension is expecting, validating the senders of incoming data, and sanitizing all inputs.

An extension should only register for runtime.onRequestExternal, if it is expecting communication from an external website or extension. Always validate that the sender matches a trusted source.

// The ID of an external extension
const kFriendlyExtensionId = "iamafriendlyextensionhereisdatas";

chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (sender.id === kFriendlyExtensionId)
doSomething();
});

Even messages via runtime.onMessage event from the extension itself should be scrutinized to ensure the MessageSender is not from a compromised content script.

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.allowedAction)
console.log("This is an allowed action.");
});

Prevent an extension from executing an attacker's script by sanitizing user inputs and incoming data, even from the extension itself and approved sources. Avoid executable APIs.

function sanitizeInput(input) {
return input.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;');
}

Last updated: Improve article

We serve cookies on this site to analyze traffic, remember your preferences, and optimize your experience.