Giovanni Ortuño Urquidi | 24dba76 | 2020-09-17 05:42:23 | [diff] [blame] | 1 | # chrome-untrusted:// FAQ |
| 2 | |
| 3 | [TOC] |
| 4 | |
| 5 | ## What is “untrustworthy content”? |
| 6 | |
| 7 | In this context, untrustworthy content is content that comes from untrustworthy sources, e.g. an image downloaded from the internet, a PDF file provided by the user, etc. Code is also considered “content” in this case. |
| 8 | |
| 9 | In general, content coming from the network is considered untrustworthy, regardless of the source and transport protocol. |
| 10 | |
| 11 | Examples of trustworthy content include, the contents of `chrome://version` which are populated entirely within the browser process, the contents of `chrome://about` which is a hardcoded list of URLs, etc. |
| 12 | |
| 13 | ## What is chrome-untrusted://? |
| 14 | |
| 15 | It is a new scheme which can be used to serve resources bundled with Chrome and that process untrustworthy content. It has the usual protections provided to `chrome://`, e.g. process isolation, but it won’t be default-granted extra capabilities that are default-granted to `chrome://`. |
| 16 | |
Carlos Knippschild | e084bd2 | 2024-09-19 18:50:54 | [diff] [blame] | 17 | The `-untrusted` suffix indicates that the [WebUI](webui_explainer.md) processes untrustworthy content. For example, rendering an image provided by users, parsing a PDF file, etc. |
Giovanni Ortuño Urquidi | 24dba76 | 2020-09-17 05:42:23 | [diff] [blame] | 18 | |
| 19 | The `-untrusted` suffix does not mean the web page is designed to do malicious things, or users should not trust it. Instead, the `-untrusted` suffix is to signal to us, Chromium developers, that this page will process untrustworthy content, and should be assumed to be compromised, much like an ordinary renderer process. |
| 20 | |
| 21 | ## Why do we need chrome-untrusted://? |
| 22 | |
| 23 | ### Separate fully trusted WebUIs and untrustworthy ones |
| 24 | |
| 25 | `chrome-untrusted://` acts as a technical and semantic boundary between fully-trusted WebUIs and untrustworthy WebUIs. |
| 26 | |
| 27 | Technical because developers can use `chrome-untrusted://` to separate their WebUIs into two origins e.g. `chrome://media-app` and `chrome-untrusted://media-app` with access to different capabilities, resources, etc. |
| 28 | |
| 29 | Semantic because it indicates to chromium developers and security reviewers that a WebUI is meant to process untrustworthy content and shouldn’t be granted dangerous capabilities. |
| 30 | |
| 31 | ### chrome:// is too powerful to process untrustworthy content |
| 32 | |
| 33 | Historically, `chrome://` pages have been built with the assumption that they are an extension to the browser process, so `chrome://` web pages are granted special capabilities not granted to ordinary web pages. For example, all `chrome://` pages can use Web APIs like camera and mic without requesting permission. |
| 34 | |
Donghan Park | ac8f6de1 | 2025-05-14 01:45:27 | [diff] [blame] | 35 | Some WebUIs would like to be able to process untrustworthy content, but granting these capabilities to a `chrome://` page would violate the [rule of 2](../security/rule-of-2.md): |
Carlos Knippschild | e084bd2 | 2024-09-19 18:50:54 | [diff] [blame] | 36 | |
Giovanni Ortuño Urquidi | 24dba76 | 2020-09-17 05:42:23 | [diff] [blame] | 37 | * a `chrome://` page is considered an extension to the browser process |
| 38 | * the renderer is written in an unsafe programming language (C++). |
Carlos Knippschild | e084bd2 | 2024-09-19 18:50:54 | [diff] [blame] | 39 | * running in an privileged context: |
Giovanni Ortuño Urquidi | 24dba76 | 2020-09-17 05:42:23 | [diff] [blame] | 40 | |
| 41 | By using `chrome-untrusted://` we put the untrustworthy content into a sandboxed and non-privileged environment (an ordinary renderer, with no dangerous capabilities). This brings us back to safety, a compromised `chrome-untrusted://` page is no worse than an ordinary web page. |
| 42 | |
| 43 | `chrome-untrusted://` re-uses a lot of the code that backs `chrome://` pages, so it doesn’t impose a big maintenance burden; even then, our hope is to one day remove all default granted capabilities based on the `chrome://` scheme to the point that the difference between `chrome://` and `chrome-untrusted://` WebUIs is just a semantic one (see previous point). |
| 44 | |
| 45 | ## When is it appropriate to use chrome-untrusted://? |
| 46 | |
| 47 | `chrome-untrusted://` is usually used for implementing privilege separation so that processing untrustworthy content e.g. parsing JSON, displaying an image, running code from the network, etc. is done in an unprivileged context. |
| 48 | |
| 49 | Today, the main use case is when we want to have code that ships with Chrome work with untrustworthy content that comes over the network. |
| 50 | |
| 51 | ## Can I use $js\_library\_from\_url? |
| 52 | |
| 53 | Yes. “Content” in this context also includes code. |
| 54 | |
| 55 | ## Do we grant any extra capabilities to chrome-untrusted://? |
| 56 | |
| 57 | Yes, but not by default and with some caveats. |
| 58 | |
| 59 | Any team that requires extra capabilities granted to `chrome-untrusted://` should consult with the security team to ensure they are non-dangerous. In this context, we consider non-dangerous any API that we would expose to the renderer process, e.g. UMA. |
| 60 | |
Jiewei Qian | b99170e | 2021-11-22 23:16:02 | [diff] [blame] | 61 | We recommend using Mojo to expose APIs to `chrome-untrusted://`. Mojo for `chrome-untrusted://` works similarly to how it works for `chrome://` with a few key differences: |
Giovanni Ortuño Urquidi | 24dba76 | 2020-09-17 05:42:23 | [diff] [blame] | 62 | |
Carlos Knippschild | e084bd2 | 2024-09-19 18:50:54 | [diff] [blame] | 63 | * Unlike `chrome://` pages, `chrome-untrusted://` pages don't get access to all renderer exposed Mojo interfaces by default. Use `PopulateChromeWebUIFrameInterfaceBrokers` to expose WebUI specific interfaces to your WebUI. See [this CL](https://crrev.com/c/3138688/5/chrome/browser/chrome_browser_interface_binders.cc) for example. |
Jiewei Qian | b99170e | 2021-11-22 23:16:02 | [diff] [blame] | 64 | * The exposed interface has a different threat model: a compromised `chrome-untrusted://` page could try to exploit the interface (e.g. sending malformed messages, closing the Mojo pipe). |
| 65 | |
Carlos Knippschild | e084bd2 | 2024-09-19 18:50:54 | [diff] [blame] | 66 | When exposing extra capabilities to `chrome-untrusted://`, keep in mind: |
Jiewei Qian | b99170e | 2021-11-22 23:16:02 | [diff] [blame] | 67 | |
| 68 | * Don't grant any capabilities that we wouldn't grant to a regular renderer. For example, don't expose unrestricted access to Bluetooth devices, but expose a method that opens a browser-controlled dialog where the user chooses a device. |
| 69 | * What you received (from the WebUI page) is untrustworthy. You must sanitize and verify its content before processing. |
| 70 | * What you send (to the WebUI page) could be exfiltrated to the Web. Don't send sensitive information (e.g. user credentials). Only send what you actually need. |
| 71 | * The difference in Mojo interface lifetimes could lead to use-after-free bugs (e.g. a page reloads itself when it shouldn't). We recommend you create and reinitialize the interface on each page load (using [WebUIPrimaryPageChanged](https://source.chromium.org/chromium/chromium/src/+/main:content/public/browser/web_ui_controller.h;l=54?q=WebUIPrimaryPageChanged&ss=chromium)), and have the JavaScript bind the interface on page load. |
| 72 | |
Carlos Knippschild | e084bd2 | 2024-09-19 18:50:54 | [diff] [blame] | 73 | We also recommend using Mojo to communicate between parent and child frames whenever possible. See [this CL](https://crrev.com/c/3222406) for example. |
Jiewei Qian | b99170e | 2021-11-22 23:16:02 | [diff] [blame] | 74 | |
| 75 | You should only use `postMessage()` when transferring objects unsupported by Mojo. For example, Media App uses `postMessage()` to pass a read-only [`FileSystemHandle`](https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API) file handle to `chrome-untrusted://media-app` from its parent `chrome://media-app`. |
| 76 | |
| 77 | We encourage teams to engage with [SECURITY_OWNERS](https://source.chromium.org/chromium/chromium/src/+/main:ipc/SECURITY_OWNERS) early and get the reviews required. |
Giovanni Ortuño Urquidi | 24dba76 | 2020-09-17 05:42:23 | [diff] [blame] | 78 | |
| 79 | ## Can chrome-untrusted:// be the main document or does it need to be embedded in a `chrome://` page? |
Jiewei Qian | 29b8e3b9 | 2021-02-17 00:22:42 | [diff] [blame] | 80 | Yes, `chrome-untrusted://` can be the main document, although the most common case is for a `chrome://` page to embed a `chrome-untrusted://` subframe. |
Giovanni Ortuño Urquidi | 24dba76 | 2020-09-17 05:42:23 | [diff] [blame] | 81 | |
| 82 | That said, the `chrome-untrusted://` scheme is an implementation detail of the WebUI and should never be shown to users. This should be factored into account when deciding whether or not to use `chrome-untrusted://` as the main document. |
| 83 | |
| 84 | ## How do I use chrome-untrusted://? |
| 85 | |
Giovanni Ortuño Urquidi | 442e8b8 | 2020-12-23 03:33:54 | [diff] [blame] | 86 | ### Create a standalone chrome-untrusted:// WebUI |
Giovanni Ortuño Urquidi | 24dba76 | 2020-09-17 05:42:23 | [diff] [blame] | 87 | |
Giovanni Ortuño Urquidi | 442e8b8 | 2020-12-23 03:33:54 | [diff] [blame] | 88 | 1. Create a class overriding `ui::WebUIConfig` and another one overriding `ui::UntrustedWebUIController` |
| 89 | |
| 90 | `WebUIConfig` contains properties for the `chrome-untrusted://` page i.e. the host and scheme. In the future, this might also contain other properties like permissions or resources. |
| 91 | |
| 92 | `UntrustedWebUIController` register the resources for the page. |
| 93 | |
| 94 | ```cpp |
| 95 | const char kUntrustedExampleHost[] = "untrusted-example"; |
| 96 | const char kUntrustedExampleURL[] = "chrome-untrusted://untrusted-example"; |
| 97 | |
John Palmer | 404a914 | 2022-12-07 00:34:48 | [diff] [blame] | 98 | class UntrustedExampleUIConfig : public content::WebUIConfig { |
Giovanni Ortuño Urquidi | 442e8b8 | 2020-12-23 03:33:54 | [diff] [blame] | 99 | public: |
| 100 | UntrustedExampleUIConfig() |
| 101 | // Set scheme and host. |
| 102 | : WebUIConfig(content::kChromeUIUntrustedScheme, kUntrustedExampleHost) {} |
| 103 | ~UntrustedExampleUIConfig() override = default; |
| 104 | |
| 105 | std::unique_ptr<content::WebUIController> CreateWebUIController( |
| 106 | content::WebUI* web_ui) override { |
| 107 | return std::make_unique<UntrustedExampleUI>(web_ui); |
| 108 | } |
| 109 | }; |
| 110 | |
| 111 | class UntrustedExampleUI : public ui::UntrustedWebUIController { |
| 112 | public: |
| 113 | UntrustedExampleUI::UntrustedExampleUI(content::WebUI* web_ui) |
| 114 | : ui::UntrustedWebUIController(web_ui) { |
| 115 | |
| 116 | // Create a URLDataSource and add resources. |
| 117 | auto* untrusted_source = |
Rebekah Potter | a89494239 | 2023-01-05 18:44:13 | [diff] [blame] | 118 | content::WebUIDataSource::CreateAndAdd( |
| 119 | web_ui->GetWebContents()->GetBrowserContext(), kUntrustedExampleURL); |
Giovanni Ortuño Urquidi | 442e8b8 | 2020-12-23 03:33:54 | [diff] [blame] | 120 | untrusted_source->AddResourcePath(...); |
Giovanni Ortuño Urquidi | 442e8b8 | 2020-12-23 03:33:54 | [diff] [blame] | 121 | } |
| 122 | |
| 123 | UntrustedExampleUI(const UntrustedExampleUI&) = delete; |
| 124 | UntrustedExampleUI& operator=(const UntrustedExampleUI&) = delete; |
| 125 | |
| 126 | UntrustedExampleUI::~UntrustedExampleUI() = default; |
| 127 | }; |
Giovanni Ortuño Urquidi | 24dba76 | 2020-09-17 05:42:23 | [diff] [blame] | 128 | |
| 129 | ``` |
Giovanni Ortuño Urquidi | 442e8b8 | 2020-12-23 03:33:54 | [diff] [blame] | 130 | |
| 131 | 2. Register the WebUIConfig |
| 132 | |
John Palmer | a6ef328 | 2022-12-07 00:34:59 | [diff] [blame] | 133 | Add the `WebUIConfig` to the appropriate list of WebUIConfigs in [`chrome_untrusted_web_ui_configs`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/webui/chrome_untrusted_web_ui_configs.cc). |
Giovanni Ortuño Urquidi | 442e8b8 | 2020-12-23 03:33:54 | [diff] [blame] | 134 | |
| 135 | ```cpp |
| 136 | register_config(std::make_unique<chromeos::UntrustedExampleUIConfig>()); |
Giovanni Ortuño Urquidi | 24dba76 | 2020-09-17 05:42:23 | [diff] [blame] | 137 | ``` |
| 138 | |
Jiewei Qian | b99170e | 2021-11-22 23:16:02 | [diff] [blame] | 139 | 3. If needed, implement and register the necessary Mojo interfaces. See [this CL](https://crrev.com/c/3138688/5/chrome/browser/chrome_browser_interface_binders.cc) for example. |
| 140 | |
Giovanni Ortuño Urquidi | 24dba76 | 2020-09-17 05:42:23 | [diff] [blame] | 141 | ### Embed chrome-untrusted:// in chrome:// WebUIs |
| 142 | |
| 143 | Developers can embed `chrome-untrusted://` iframes in `chrome://` pages. [Example CL](https://chromium-review.googlesource.com/c/chromium/src/+/2037186). |
| 144 | |
| 145 | The general steps are: |
Giovanni Ortuño Urquidi | 442e8b8 | 2020-12-23 03:33:54 | [diff] [blame] | 146 | 1. Create a WebUIConfig and UntrustedWebUIController to register the resources for the `chrome-untrusted://` page. |
Giovanni Ortuño Urquidi | 24dba76 | 2020-09-17 05:42:23 | [diff] [blame] | 147 | 2. Allow the `chrome://` WebUI to embed the corresponding `chrome-untrusted://` WebUI. |
Giovanni Ortuño Urquidi | 442e8b8 | 2020-12-23 03:33:54 | [diff] [blame] | 148 | ```cpp |
Giovanni Ortuño Urquidi | 24dba76 | 2020-09-17 05:42:23 | [diff] [blame] | 149 | untrusted_data_source->AddFrameAncestor(kWebUIPageURL) |
| 150 | ``` |
| 151 | 3. Make `chrome-untrusted://` requestable by the main `chrome://` WebUI. |
Giovanni Ortuño Urquidi | 442e8b8 | 2020-12-23 03:33:54 | [diff] [blame] | 152 | ```cpp |
Giovanni Ortuño Urquidi | 24dba76 | 2020-09-17 05:42:23 | [diff] [blame] | 153 | web_ui->AddRequestableScheme(content::kChromeUIUntrustedScheme) |
| 154 | ``` |
| 155 | 4. Allow the `chrome://` WebUI to embed chrome-untrusted://. |
Giovanni Ortuño Urquidi | 442e8b8 | 2020-12-23 03:33:54 | [diff] [blame] | 156 | ```cpp |
Giovanni Ortuño Urquidi | 24dba76 | 2020-09-17 05:42:23 | [diff] [blame] | 157 | trusted_data_source->OverrideContentSecurityPolicy( |
Giovanni Ortuño Urquidi | 442e8b8 | 2020-12-23 03:33:54 | [diff] [blame] | 158 | “frame-src ” + kUntrustedExampleURL); |
Giovanni Ortuño Urquidi | 24dba76 | 2020-09-17 05:42:23 | [diff] [blame] | 159 | ``` |
Jiewei Qian | b99170e | 2021-11-22 23:16:02 | [diff] [blame] | 160 | 5. Add communication mechanism to `chrome-untrusted://` frames. For example, [using Mojo](https://crrev.com/c/3222406), or `postMessage` the JavaScript object is not supported by Mojo. |