Improving Extension Security in Manifest V3

manifest v3

Improving security in Manifest V3. Manifest V3 introduces changes aimed at enhancing extension security. This section outlines steps to bolster the security of your extensions, focusing on code not part of the extension service worker. It covers removing the execution of arbitrary strings, eliminating remotely hosted code, updating content security policies, and more. Created by Exmo – a monetization platform for browser extensions.

Previos posts:

Note: Manifest V3 is typically supported in Chrome 88 or later. For features introduced in subsequent Chrome versions, refer to the API reference documentation to verify support information. If your extension relies on a particular API, you can specify a minimum Chrome version in the manifest file.

Remove Execution of Arbitrary Strings

Manifest V3 prohibits executing external logic using methods like executeScript(), eval(), and new Function(). To comply with this, follow these steps:

  1. Move all external code (JavaScript, WebAssembly, CSS) into your extension bundle.
  2. Update script and style references to load resources from the extension bundle.
  3. Use chrome.runtime.getURL() to build resource URLs at runtime.
  4. Utilize a sandboxed iframe where necessary, as eval() and new Function(...) are still supported within sandboxed iframes.

Note: executeScript() is now part of the scripting namespace instead of the tabs namespace. Refer to the Move executeScript() guide for updating calls.

However, some exceptions allow executing arbitrary strings, such as injecting remote hosted stylesheets, using chrome.devtools.inspectWindow.eval, and chrome.debugger.sendCommand for debugger extensions.


Remove Remotely Hosted Code

Manifest V3 mandates that all extension logic must reside within the extension package. This means you cannot load and execute remotely hosted files. Common examples include JavaScript files from developer servers or libraries hosted on CDNs.

Alternative approaches include:

  • Configuration-driven features and logic
  • Externalized logic with a remote service
  • Embedding remotely hosted code in a sandboxed iframe
  • Bundling third-party libraries locally

Refer to the guidelines for handling external libraries in tab-injected scripts and injecting functions for dynamic behavior.

Configuration-driven features and logic: 

Your extension can implement configuration-driven features by loading and caching a remote configuration file, such as a JSON file, during runtime. This cached configuration determines which features are enabled or disabled based on the defined settings.

Externalized logic with a remote service

Utilize a remote web service for externalized logic in your extension. By calling a remote web service, you can keep sensitive code private and easily modify it as needed without the hassle of resubmitting to the Chrome Web Store. This approach minimizes the overhead associated with managing code changes.

Embed remotely hosted code in a sandboxed iframe

Remotely hosted code can be embedded within sandboxed iframes in your extension. However, it’s important to note that this method may not work if the embedded code requires access to the embedding page’s Document Object Model (DOM). Sandboxed iframes provide a secure environment for executing remotely hosted code while isolating it from the main page’s DOM.

Bundle third-party libraries

If your extension relies on third-party libraries, consider bundling them locally instead of loading them from external servers. For popular frameworks like React or Bootstrap, download the minified files and include them directly in your extension project. By importing these libraries locally, you reduce dependency on external servers and ensure consistent availability of resources. For example:

<script src="./react-dom.production.min.js"></script>
<link href="./bootstrap.min.css" rel="stylesheet">

To include a library in a service worker, configure the “background.type” key in the manifest file to “module” and utilize an import statement.

Caution: Certain libraries, such as Firebase, may dynamically fetch additional code at runtime. To address this, you have two options. First, opt for a library that does not rely on remotely-hosted code. Alternatively, you can download all potential dynamic imports during the build process. Below is an example script demonstrating how to bundle Firebase using Rollup.js.

Use external libraries in tab-injected scripts

You can still load external libraries dynamically in tab-injected scripts by including them in the files array when using scripting.executeScript(). This allows you to load data remotely at runtime.

chrome.scripting.executeScript({
  target: {tabId: tab.id},
  files: ['jquery-min.js', 'content-script.js']
});

Inject a function

To inject greater dynamism, you can utilize the func property within scripting.executeScript() to inject a function as a content script and pass variables using the args property.

Manifest V2

let name = 'World!';
chrome.tabs.executeScript({
  code: `alert('Hello, ${name}!')`
});

In a background script file.

Manifest V3

async function getCurrentTab() {/* ... */}
let tab = await getCurrentTab();
function showAlert(givenName) {
  alert(`Hello, ${givenName}`);
}

let name = 'World';
chrome.scripting.executeScript({
  target: {tabId: tab.id},
  func: showAlert,
  args: [name],
});

In the background service worker.

You can explore a function injection example available in the Chrome Extension Samples repository. Additionally, you can find a reference for the getCurrentTab() function.

Look for other workarounds

If the aforementioned methods don’t address your specific use case, you may need to explore alternative solutions. This could involve migrating to a different library that better suits your needs or finding alternative ways to leverage the functionality of the existing library. For instance, in the case of Google Analytics, you might consider switching to the Google Measurement Protocol instead of relying on the official remotely-hosted JavaScript version, as detailed in our Google Analytics 4 guide.


Update the content security policy

The “content_security_policy” remains a part of the manifest.json file in Manifest V3, but it is now structured as a dictionary supporting two properties: “extension_pages” and “sandbox“.

In Manifest V2, it was typically defined as follows:

{
  ...
  "content_security_policy": "default-src 'self'"
  ...
}

In Manifest V3, it is structured like this:

{
  ...
  "content_security_policy": {
    "extension_pages": "default-src 'self'",
    "sandbox": "..."
  }
  ...
}

The “extension_pages” property refers to contexts within your extension, such as HTML files and service workers.

Note: These page types are served from the chrome-extension:// protocol. For example, a page in your extension would be accessed via chrome-extension://EXTENSION_ID/foo.html.

sandbox: Applies to any sandboxed extension pages used by your extension.


Remove Unsupported Content Security Policies

Manifest V3 disallows certain content security policy values in the “extension_pages” field that were permitted in Manifest V2, specifically those that allow remote code execution. The script-src, object-src, and worker-src directives are limited to the following values:

Note that content security policy values for sandbox do not have these new restrictions.

Migrate Manifest v2 to v3

Frequently Asked Questions

Why is security important in Manifest V3?

Manifest V3 introduces stricter security measures to mitigate potential risks and enhance user privacy and safety. By adhering to these security standards, developers can create more secure extensions that protect user data and ensure a safer browsing experience.

What are the key changes in security between Manifest V2 and V3?

Manifest V3 prohibits executing external logic using methods like executeScript(), eval(), and new Function(). It also mandates that all extension logic must reside within the extension package, disallowing the loading and execution of remotely hosted files. Additionally, Manifest V3 introduces updates to content security policies to further enhance security.

How can I remove the execution of arbitrary strings in Manifest V3?

To comply with Manifest V3 requirements, developers should move all external code into the extension bundle, update script and style references to load resources from the bundle, and utilize chrome.runtime.getURL() to build resource URLs at runtime. Additionally, consider using sandboxed iframes where necessary.

What are the alternatives to loading remotely hosted code in Manifest V3?

Instead of loading remotely hosted code, developers can implement configuration-driven features and logic, utilize externalized logic with a remote service, embed remotely hosted code in sandboxed iframes, or bundle third-party libraries locally. These approaches ensure that all extension logic resides within the extension package, enhancing security.

How can I update the content security policy in Manifest V3?

In Manifest V3, developers should update the content security policy by structuring it as a dictionary with properties for “extension_pages” and “sandbox”. It’s important to remove unsupported content security policy values that allow remote code execution and ensure that only permitted values are used to maintain security standards.

ruslana-gonsalez

As an Exmo Product Manager, my role involves overseeing the development and enhancement of our monetization platform. I lead a team of specialists, strategizing innovative features and improvements to optimize user experience. My responsibilities include conducting market research, gathering user feedback, and collaborating with developers to ensure Exmo remains at the forefront of browser extension monetization.