Extension Permissions

Summary

Extension Permissions are used to determine which capabilities are exposed to a given extension. They are specified in the extension’s manifest.json file.

This document describes the internal implementation of extension permissions. For more general extension permission information, see the public documentation on declaring permissions and permission warnings.

Types of Permissions

There are three basic classes of permissions.

API Permissions

API permissions are typically used to grant an extension access to a specific API. These are usually specified by a reserved string (like the API name). Examples include tabs, cookies, storage, and others.

Match Patterns

Match patterns provide access to a set of urls. Match patterns can encompass multiple hosts, so a single pattern may provide access to many different domains (or even, every domain). For more information, see the public match pattern documentation. In the codebase, match patterns are implemented as URLPattern.

Explicit Hosts

Explicit host permissions are match patterns specified in the permissions key of the extension’s manifest. These patterns control access to APIs that read or modify host data, such as cookies, webRequest, and tabs.executeScript.

Note: Any path component is currently ignored for explicit hosts in permissions parsing.

Scriptable Hosts

Scriptable hosts are match patterns used in the content_scripts entry of the extension’s manifest. These only control which sites an extension can use content scripts on, and do not affect any other API.

From a messaging standpoint, we treat both explicit hosts and scriptable hosts the same (with each allowing the extension to read and modify data on one or more domains). The distinction is only to provide granular permissions in order to restrict what we provide the extension.

Manifest Permissions

A manifest permission is a specified manifest entry that provides an extension with some permission. Examples of this include page overrides (to allow extensions to override a page, like the New Tab Page), externally_connectable (allowing extensions to communicate with websites or extensions), and bluetooth (allowing the extension to communicate with specific bluetooth devices). Manifest permissions are effectively similar to API permissions in that they provide access to a certain capability. From an implementation standpoint, permissions can often be implemented as either an API Permission or a Manifest Permission. For the rest of this document, we group both API and manifest permissions into “API permissions”.

Storing Permissions

Permissions are stored in the Preferences file, under the extension’s entry. We always store two different sets of permissions: granted permissions and active permissions (with the runtime host permissions feature, we will store a third, runtime granted permissions - this is described below). Note that neither of these include any non-persistent permissions.

Granted Permissions

Granted permissions are the permissions that the extension has ever been granted by the user (and have not been revoked by the user). When the user installs an extension, we grant the accepted permissions. If the extension is granted more permissions at any point, we update the set of granted permissions to include those. Extensions can be granted additional permissions through either an extension update that requires new permissions (assuming the user agrees) or through the permissions API (which is used for optional permissions). Granted permissions can be thought of as the maximum level of permissions an extension may have.

Active Permissions

Active permissions are the permissions that an extension currently has. This set is used to initialize extension permission state when the extension is loaded, and determines the APIs and domains an extension can access.

Differences

The sets of active permissions and granted permissions can differ in a few scenarios.

  • Extension Versions: Consider a scenario in which version 1 of an extension requires permission “foo”, and version 2 of the extension removes permission “foo”. If the user installed version 1, then they granted the extension permission “foo” (at installation). When the user is upgraded to version 2, the extension removes the permission, and so the active permissions change (reflecting the permissions the extension currently has). However, the granted permissions do not change, and still include “foo”.
  • Optional Permissions: Extensions can use the permissions API to request optional permissions. When an extension first requests a permission (using chrome.permissions.request()), the user is prompted to accept or refuse it. If the user accepts, the permission is added to both the active and granted permissions. The extension can then remove this permission using chrome.permissions.remove(). Removing the permission removes it from the active permissions (reducing the extension’s immediate capabilities), but does not remove it from the granted permissions. If the extension uses chrome.permissions.request() for the same permission after removing it, the user will not be prompted again because the permission is present in the granted permissions.

Non-Persistent Permissions

Some permissions are granted ephemerally, and are not stored or persisted. These are tab-specific permissions in the code, and are used with the activeTab permission. This provides the extension with temporary access to a tab, so that it can use chrome.tabs.executeScript(), the webRequest API, see the tab's URL and favicon with the tabs API, and capture the screen with the chrome.tabs.captureTab() function. When the user closes or navigates the tab, the permission is revoked.

Permission Increases

When an extension is updated to a new version, the new version can include new permissions. Chrome will disable the extension if it is detected that the new version contains higher privilege than what is currently granted to the extension (see also Determining Privilege Increase). Note that this compares the new set of requested permissions to the granted permissions, rather than the active permissions for the extension. This distinction is important when the granted set is different than the active set. Consider the two following cases:

  • An extension has permission “foo” in version 1, removes it in version 2, and re-adds it in version 3. This extension will not be disabled for any users that installed the extension at version 1, because those users already granted the extension permission “foo”. The extension will be disabled for any users who only installed version 2 (and not version 1) of the extension, since this version did not include the “foo” permission.
  • An extension that has been granted permission “foo” through the permissions API and then adds permission “foo” as a required permission in an update. This extension will not be disabled for users who granted the permission, and will be disabled for any users that did not grant permission “foo”.

Determining Permission Warnings

Most permissions have an associated warning string, which describes what the permission allows the extension to do. Permission warnings are shown to the user at a number of different times:

  • Installation
  • Requesting optional permissions
  • Updating to a new version that requires more permissions
  • Viewing an installed extension’s details

In order to maximize the benefit to users, Chrome tries to show as few warnings as possible, while ensuring they are still accurate.

Messageless Permissions

Some permissions (like the storage API) are considered relatively harmless, and will not generate a permission warning.

Permission Collapsing

Certain permissions “contain” or imply other permissions. With host permissions, this is easy to understand: if an extension has access to all google.com sites (i.e., *://*.google.com/*), requesting access to a specific google.com site (https://maps.google.com/*) is redundant. We should not warn the user that the extension wants to both “access all google.com sites” and “access maps.google.com”.

API permissions can be similarly redundant, if one API provides the same or more privilege than another. For instance, if an extension has access to a user’s history, then it implicitly has access to the user’s top sites. Thus, if an extension requests both the history permission and the topSites permission, we will only display a single warning to the user.

Determining Privilege Increase

Not all permission changes result in disabling the extension. In order to calculate if an extension has escalated privilege and should be disabled, Chrome compares the permission messages that are associated with the granted permissions and the newly-requested permissions. This is different than comparing the permissions themselves because of messageless permissions and permission collapsing.

This has implications when determining privilege increase. If an extension adds new permissions but all of those permissions are either a) messageless or b) collapsed into already-granted permissions, then the extension is not disabled. Otherwise, Chrome considers it a privilege increase, disables the extension, and notifies the user (allowing them to accept the new permissions and re-enable it, if they want).

Note: since optional permissions are not granted automatically, adding optional permissions in a new version will never disable the extension.

Sync

Granted permissions are not currently synced. Instead, we sync the disable reasons for an extension (which can include that an extension was disabled for a permissions increase) and the extension version. In most cases, this allows users to approve an extension only once and have it enabled on all devices. When the extension is updated to a new version, if it has increased permissions, it will be disabled on the device. The user can then approve the new permissions, which enables the extension. When the user goes to a new device, the extension will be updated, and Chrome will notice the increased permissions, but since the extension has been approved in sync, it will not be disabled.

Known Issues

There are two known issues related to syncing extensions:

  • If the extension is updated and approved on device A, and then updated on device B before sync is applied, it can be disabled on device B for a period of time (until sync finishes applying). This can result in a temporary, user-visible error, which then disappears.
  • Consider the scenario in which an extension is installed at version 1 on device A. The user doesn‘t sign into device A for a period of time, and version 2 of the extension is published (but never installed on device A). The user then signs into device B. Version 2 of the extension will be installed (because it is the latest version pulled from the store); however, sync only knows that version 1 has been approved (and doesn’t know about version 2). Chrome will disable version 2 of the extension on device B, even if no new permissions are added. This is because Chrome cannot know whether version 2 added new permissions or not, and behaves conservatively. Syncing granted permissions would solve this issue. See https://crbug.com/809797.

Runtime Host Permissions

Runtime host permissions is a new feature that allows users to have more control over when and where their extensions run. This allows the user to install an extension that requests permission to multiple hosts, but have a choice over if it runs.

The runtime host permissions feature introduces a number of permissions-related concepts.

Withheld Permissions

Withheld permissions are stored on the Extension object (as part of the PermissionsData class). Withheld permissions are the set of permissions that were listed as required permissions by the extension, but were withheld as part of the runtime host permissions feature. The extension does not have acccess to these permissions.

Runtime-Granted Permissions

Runtime-granted permissions are stored within extension preferences, and represent the permissions that the user has granted the extension at runtime. This is deliberately kept as a separate set from granted permissions in order to ensure that experimentation with the feature is independent, and will not affect extensions when the feature is disabled.

Runtime-granted permissions include permissions granted through dedicated UI for the runtime host permissions feature (such as context menu controls and controls in the chrome://extensions page) as well as optional permissions granted through the permissions API.

The controls for granting runtime permissions allow granting permissions beyond what the extension specifically requests. This is so that the user can grant https://google.com/*, even if the extension only requested https://google.com/maps. This is useful for two reasons. First, it presents a simpler UI to the user (who doesn't need to worry about granting exactly the correct URL pattern); secondly, it means if the extension later requests an additional pattern within the same host, it will be automatically granted.

However, though the runtime-granted permissions may extend beyond what is explicitly requested, the current permissions on the extension object itself (or granted to the extension process) should not. This provides us increased security, since we don't want to have extensions privileged beyond what they should need.

Calculating Current Permissions

With the runtime host permissions feature, calculating current permissions is a little more complex. Typically, an extension's current permissions are calculated as

current_permissions =
    intersection(active_permissions,
                 union(required_permissions, optional_permissions))

Said differently, an extension‘s permissions are equal to any permissions in the active permission set that also appear in either the required or optional sets. This ensures that the extension never has access to permissions that it didn’t request, which is important for security reasons (we don't want an over-privileged process if we can avoid it).

With runtime host permissions, this calculation is a little more difficult:

current_permissions_without_feature =
    intersection(active_permissions,
                 union(required_permissions, optional_permissions))
current_permissions =
    intersection(current_permissions_without_feature,
                 runtime_granted_permissions)

The system will withhold permissions that were not within the set of runtime-granted permissions. As noted above, the runtime-granted permissions may include more than what was explicitly requested by the extension; however, we will not extend these permissions to the extension object because these additional permissions will not be present on in the required or optional permissions.

Permissions Intersections

With runtime host permissions, it's possible that the user will grant a host permission that overlaps with a requested host, but is neither a direct match nor a strict subset. For instance, an extension may request the pattern *://google.com/maps, and a user may grant https://*.google.com/*. In this case, we should neither grant *://google.com/maps (which includes origins the user did not approve, nor grant https://*.google.com/* (which includes more than the extension requires). Instead, we should grant the extension https://google.com/maps - the intersection of the granted permission and the requested permission.

We perform this calculation with runtime host permissions. This has the implication that permissions granted to the extension object may not be explicitly present within the extension's required or optional permissions, but rather contained by one of those sets.