CRX API Security Checklist

Contact: security-dev@chromium.org

The following is a list of security points to mind when developing Chrome extension / app features. You should be able to quickly skim and learn about common patterns that have security implications.

Note: we use the term feature in the Doc to mean (1) An extension or app API in the form of chrome.foo(params), or (2) any functionality that modifies Chrome UI or settings and is available to extensions and apps.

Prefer chooser dialogs over install-time warnings.

Follow the principle of least privilege.

Consider adding a UI indicator when your feature is running.

Make your UI user friendly.

Respect incognito mode and multiple profiles.

Provide a way to revert changes to browser or OS settings.

Inform the user when granting implicit access to resources.

Prefer HTTPS over HTTP.

Consider adding an enterprise policy for your feature.

Prefer declarative APIs.

Mitigate denial of service (DOS) attacks that can be caused by your feature.

Avoid altogether or restrict the scope of non-public APIs.

Don’t make spoofing and phishing easier.

Don’t bypass the same origin policy without host permissions.

Don’t run on chrome:// URLs by default.


Prefer chooser dialogs over install-time warnings.

Choosers give finer control to the user and should be preferred when possible.

Examples:

Follow the principle of least privilege.

One should be able to grep manifest.json to see if an extension or app uses your feature without having to run the CRX.

If a permission is required to access your feature, it should also work as an optional permission. See advantages of optional permissions.

If your feature requests permission to interact with hardware such as USB or bluetooth, require the app to explicitly list device and vendor IDs. Also consider adding the device name in the permission warning.
Example: Prefer “Access USB devices including <Foo USB Bar>” over “Access USB devices”.

Example: Consider a hypothetical permission warning “permanently access stored data”: Why is the extension asking for permanent access? Is temporary access already granted? What can go wrong with permanent access that wasn’t already possible with temporary access? If the answers aren’t obvious, you may not need a warning. Instead, consider adding a UI to control permanent access.

Consider adding a UI indicator when your feature is running.

If your feature has privacy implications and can be started/stopped, let the user know when it is running by way of a UI indicator and allow the user to control the feature through this UI. A UI indicator can be in the form of browser action icon, page action icon, tab icon, popup, screen overlay or infobar. Some privileges might not require a permission warning if you have a UI surface (e.g., chrome.notifications).

Example: chrome.desktopCapture displays an overlay while sharing the desktop:

Make your UI user friendly.

If your feature creates a confirmation dialog, consider adding the name of the extension or the app to the dialog so that the user can understand it’s triggered by an extension or app and not Chrome. Be careful with the wording: an extension shouldn’t trick the user with a well-chosen name.

Example: The confirmation dialog for chrome.management.uninstall displays both triggering and target extension names.

Example: If your feature is capturing the screen and sharing with the page it’s running on, mention both extension name and the page origin on your UI.

Respect incognito mode and multiple profiles.

Extensions and apps are specific to profiles. If your feature should run across profiles (including incognito), allow the extension developer or the user to choose the scope. Consider adding a parameter to set the scope of the API.
Example: types API has a |scope| parameter that distinguishes how the changes should apply (only to regular sessions, both regular and incognito sessions etc).

Provide a way to revert changes to browser or OS settings.

Example: A hypothetical display settings API can set screen brightness. Users can recover from extreme brightness setting by uninstalling the app.

Inform the user when granting implicit access to resources.

It may be difficult for users to understand how granting access works with hierarchical resources such as domains, iframes or filesystems. If your feature gets access to a resource and all its sub resources, inform the user.

Prefer HTTPS over HTTP.

Connect Google services only over HTTPS. Consider HTTP deprecated.

Consider adding an enterprise policy for your feature.

If you think IT departments might want to block your feature because it’s too powerful, can have privacy implications or incompatibilities, consider adding a policy entry for it.

The necessary steps to add a new policy setting to Chromium are explained here in detail.

Prefer declarative APIs.

Declarative APIs are more auditable. If you have a getter/setter type of API, consider making it declarative.

Example: You are implementing a hypothetical feature that sets a Windows registry key. Instead of chrome.registry.set(path, value), prefer having a manifest entry as key value pairs:

{ “name”: “Extension”,

  “version”: “1.0.0.0”,

  ...

  “registry”: {

    “path/to/key1”: “value1”,
   “path/to/key2”: “value2”

 }}

Mitigate denial of service (DOS) attacks that can be caused by your feature.

DOS prevents the user from using the extension, web page, browser or the OS. Some examples of DOS attacks by extensions are constantly triggering a native dialog until the user accepts it, closing all tabs (disabling the browser), reloading tabs (disabling the page) or reloading another extension (disabling the extension).

Consider doing these to prevent this class of DOS:

Avoid altogether or restrict the scope of non-public APIs.

Before implementing a non-public API, see if you can create an app/extension pair and pass around messages to achieve what you want. If yes, prefer doing that.

Example: You are implementing an extension API that needs access to UDP over native sockets for a protocol called foo://. Don’t whitelist chrome.sockets.* for your extension. Create a chrome.fooPrivate namespace and implement the bare minimum access to native sockets (no TCP, only specific port/host pair allowed, strict checks for the foo:// protocol etc).

Don’t make spoofing and phishing easier.

Phishing is one of the leading ways users are harmed on the Internet, so consider how your API may be abused by attackers. Here are some common things to avoid:

Don’t bypass the same origin policy without host permissions.

Require a host permission if your API can read site content in any way, including but not limited to reading the DOM, XHR responses or pixels.[4]

Having host permission for the site in the top level frame doesn’t imply permission to the embedded resources. Embedding an [iframe|svg|img|...] with src=http://example.com into a chrome-extension://<your-extension-id>/page.html should never result in ability to read data from example.com.

Example: Taking a screenshot of the current tab should require all hosts permissions or a window chooser dialog that requires confirmation from the user.

Don’t run on chrome:// URLs by default.

Reading or modifying chrome:// pages can result in capabilities that you didn’t actually ask for. Be conservative and don’t run on internal pages by default. [5]
Example: Being able to take a screenshot of chrome://history/ page effectively means granting the extension/app "history" permission.


[1] [Link] Not all permissions need warnings. If there are no security, privacy or usability implications, consider not showing a warning.

[2] Bug to automate this: http://crbug.com/344566

[3] Apps, by design, can fully control created windows so this point doesn’t strictly apply to them.

[4] Some properties such as page title, favicon or url may require permissions such as “favicon” or “history” instead of site permission.

[5] This may eventually be mandatory: http://crbug.com/172375