| # Threat Model And Defenses Against Compromised Renderers |
| |
| Given the complexity of the browser, our threat model must use a "defense |
| in depth" approach to limit the damage that occurs if an attacker |
| finds a way around the Same Origin Policy or other security logic in the |
| renderer process. |
| For example, the combination of Chrome's sandbox, IPC security checks, and Site |
| Isolation limit what an untrustworthy renderer process can do. They |
| protect Chrome users against attackers, even when such attackers are able to |
| bypass security logic in the renderer process. |
| For other arguments for the "defense in depth" approach and why our |
| threat model covers compromised renderers, please see |
| [the Site Isolation motivation](https://www.chromium.org/Home/chromium-security/site-isolation#TOC-Motivation). |
| |
| In a compromised renderer, an attacker is able to execute |
| arbitrary native (i.e. non-JavaScript) code within the renderer |
| process's sandbox. A compromised renderer can forge |
| malicious IPC messages, impersonate a Chrome Extension content script, |
| or use other techniques to trick more privileged parts of the browser. |
| |
| The document below gives an overview of features that Chrome attempts to |
| protect against attacks from a compromised renderer. Newly discovered |
| holes in this protection would be considered security bugs and possibly |
| eligible for the |
| [Chrome Vulnerability Rewards Program](https://www.google.com/about/appsecurity/chrome-rewards/). |
| |
| [TOC] |
| |
| |
| ## Site Isolation foundations |
| |
| Most of the other protections listed in this document implicitly assume that |
| attacker-controlled execution contexts (e.g. HTML documents or service workers) |
| are hosted in a separate renderer process from other, victim contexts. |
| This separation is called |
| [Site Isolation](https://www.chromium.org/Home/chromium-security/site-isolation) |
| and allows the privileged browser |
| process to restrict what origins a renderer process is authorized to read or |
| control. |
| |
| The privilege restriction can be implemented in various ways - see the |
| "protection techniques" listed in other sections in this document. |
| One example is validating in the browser process whether an incoming IPC can |
| legitimately claim authority over a given origin (e.g. by checking via |
| `CanAccessDataForOrigin` if the process lock matches). |
| Another example is making sure that capabilities handed over to renderer |
| processes are origin-bound (e.g. by setting `request_initiator_origin_lock` |
| on a `URLLoaderFactory` given to renderer processes). |
| Yet another example is making security decisions based on trustworthy knowledge, |
| calculated within the privileged browser process (e.g. using |
| `RenderFrameHost::GetLastCommittedOrigin()`). |
| |
| Compromised renderers shouldn’t be able to commit an execution context |
| (e.g. commit a navigation to a HTML document, or create a service worker) |
| in a renderer process hosting other, cross-site execution contexts. |
| On desktop platforms all sites (site = scheme plus eTLD+1) should be isolated |
| from each other. |
| On Android, sites where the user entered a password should be isolated |
| from each other and from other sites. |
| |
| **Known gaps in protection**: |
| - No form of Site Isolation is active in Android WebView. |
| See also https://crbug.com/769449. |
| - Frames with `<iframe sandbox>` attribute are not isolated |
| from their non-opaque precursor origin. |
| See also https://crbug.com/510122. |
| - `file:` frames may share a process with other `file:` frames. |
| See also https://crbug.com/780770. |
| |
| |
| ## Cross-Origin HTTP resources |
| |
| Compromised renderers shouldn't be able to read the contents (header + body) of |
| a cross-site HTTP response, unless it is a valid subresource needed for |
| compatibility (e.g., JavaScript, images, etc), or is successfully allowed via |
| CORS. |
| |
| Protection techniques: |
| - Enforcing |
| [Cross-Origin Read Blocking |
| (CORB)](https://www.chromium.org/Home/chromium-security/corb-for-developers) |
| in the NetworkService process |
| (i.e. before the HTTP response is handed out to the renderer process). |
| - Only allowing the privileged browser process to create |
| `network::mojom::URLLoaderFactory` objects that handle HTTP requests. |
| This lets the browser process carefully control security-sensitive |
| `network::mojom::URLLoaderFactoryParams` of such factories (such as |
| `request_initiator_origin_lock`, `is_orb_enabled`, `disable_web_security` or |
| `isolation_info`). |
| |
| **Known gaps in protection**: |
| - Content types for which CORB does not apply |
| (e.g. `image/png`, `application/octet-stream`) are not protected by |
| default. We recommend that HTTP servers protect such resources by |
| either serving a `Cross-Origin-Resource-Policy: same-origin` response header |
| or validating the `Sec-Fetch-Site` request header. |
| |
| |
| ## Contents of cross-site frames |
| |
| Compromised renderers shouldn't be able to read the contents of cross-site |
| frames. Examples: |
| - Text or pixels of cross-site frames. |
| - Full URL (e.g. URL path or query) of cross-site frames. |
| Note that the origin of other frames |
| needs to be exposed via `window.origin` for legacy reasons. |
| |
| Protection techniques: |
| - Compositing tab contents (both for display and for printing) |
| outside the renderer processes. |
| - Isolating PDF plugins. |
| - Being careful what URLs are exposed in console messages. |
| |
| **Known gaps in protection**: |
| - Mixed content console messages may disclose cross-site URLs |
| (see also https://crbug.com/726178). |
| |
| |
| ## Cookies |
| |
| Compromised renderers shouldn’t be able to read or write |
| any cookies of another site, |
| or `httpOnly` cookies even from the same site. |
| |
| Protection techniques: |
| - Renderer processes are only given `network::mojom::RestrictedCookieManager` |
| for origins within their site |
| (see `StoragePartitionImpl::CreateRestrictedCookieManager`). |
| - Mojo serialization does not send any cookies from HTTP headers to the renderer |
| process (see |
| `ParamTraits<scoped_refptr<net::HttpResponseHeaders>>::Write`). |
| |
| |
| ## Passwords |
| |
| Compromised renderers shouldn’t be able to read or write passwords of |
| other sites. |
| |
| Protection techniques: |
| - Using `CanAccessDataForOrigin` to verify IPCs sent by a renderer process |
| (e.g. `//components/password_manager/content/browser/bad_message.cc`) |
| - Using trustworthy, browser-side knowledge |
| to determine which credentials to read or write |
| (e.g. `content::RenderFrameHost::GetLastCommittedURL` in |
| `password_manager::CredentialManagerImpl::GetOrigin`). |
| |
| |
| ## Security-sensitive UI/chrome elements (e.g. Omnibox) |
| |
| Compromised renderers shouldn’t be able to influence/spoof |
| security-sensitive UI elements. |
| |
| Examples: |
| - Omnibox |
| - URL (e.g. renderer process locked to foo.com shouldn’t |
| be able to trick the Omnibox into displaying bar.com) |
| - Secure / not secure chip (e.g. a renderer process locked to a HTTP |
| site shouldn’t be able to trick the Omnibox into displaying a |
| HTTPS-associated lock) |
| - Content settings (e.g. a renderer process that has been granted |
| microphone access shouldn’t be able to suppress the mic/camera |
| icon in the Omnibox) |
| - Dialogs and prompts (for example a permissions dialog asking to allow |
| a site to show notifications) |
| - Origin in dialogs (e.g. a renderer process locked to foo.com |
| shouldn’t be able to trick the Omnibox into displaying a bar.com |
| URL in permission dialogs) |
| |
| Protection techniques: |
| - `RenderFrameHostImpl::CanCommitOriginAndUrl` verifies that the renderer |
| process is able to commit what it claims, and kills the process otherwise. |
| - Work-in-progress: calculating the origin in the browser process, |
| before a navigation commits (https://crbug.com/888079). |
| |
| |
| ## Permissions |
| |
| Compromised renderers shouldn’t be able to gain permissions without user |
| consent. |
| |
| Examples: microphone access permission, geolocation permission, etc. |
| |
| Protection techniques: |
| - Requesting permissions based on browser-side knowledge of frame's origin |
| (e.g. see `GeolocationServiceImplContext::RequestPermission`). |
| |
| |
| ## Web storage |
| |
| Compromised renderers shouldn’t be able to read from or write into |
| storage of another site. |
| |
| Examples of protected storage technologies: |
| - localStorage |
| - sessionStorage |
| - indexedDB |
| - blob storage |
| - webSQL |
| |
| Protection techniques: |
| - Using `CanAccessDataForOrigin` to verify IPCs sent by a renderer process |
| (e.g. see `StoragePartitionImpl::OpenLocalStorage`). |
| - Binding Mojo interfaces to a single origin obtained from browser-side |
| information in `RenderFrameHost::GetLastCommittedOrigin()` |
| (e.g. see `RenderFrameHostImpl::CreateIDBFactory`). |
| |
| |
| ## Messaging |
| |
| Compromised renderers shouldn’t be able to: |
| - Spoof the `MessageEvent.origin` seen by a recipient of a `postMessage`. |
| - Bypass enforcement of the `targetOrigin` argument of `postMessage`. |
| - Send or receive `BroadcastChannel` messages for another origin. |
| - Spoof the `MessageSender.url`, nor `MessageSender.origin`, nor |
| `MessageSender.id` (i.e. an extension id which can differ from the origin when |
| the message is sent from a content script), as seen by a recipient of a |
| `chrome.runtime.sendMessage`. |
| See also [MessageSender documentation](https://developers.chrome.com/extensions/runtime#type-MessageSender) and [content script security guidance](https://groups.google.com/a/chromium.org/forum/#!topic/chromium-extensions/0ei-UCHNm34). |
| - Spoof the id of a Chrome extension initiating |
| [native messaging](https://developer.chrome.com/docs/apps/nativeMessaging/) |
| communication. |
| |
| Protection techniques: |
| - Using `CanAccessDataForOrigin` to verify IPCs sent by a renderer process |
| (e.g. in `RenderFrameProxyHost::OnRouteMessageEvent` or |
| `BroadcastChannelProvider::ConnectToChannel`). |
| - Using `ContentScriptTracker` to check if IPCs from a given renderer process |
| can legitimately claim to act on behalf content scripts of a given extension. |
| |
| |
| ## JavaScript code cache |
| |
| Compromised renderers shouldn't be able to poison the JavaScript code cache |
| used by scripts executed in cross-site execution contexts. |
| |
| Protection techniques: |
| - Using trustworthy, browser-side origin lock while writing to and fetching from |
| the code cache by using `ChildProcessSecurityPolicyImpl::GetOriginLock` in |
| `GetSecondaryKeyForCodeCache` in |
| `//content/browser/renderer_host/code_cache_host_impl.cc` |
| |
| |
| ## Cross-Origin-Resource-Policy response header |
| |
| A compromised renderer shouldn’t be able to bypass |
| [Cross-Origin-Resource-Policy (CORP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin_Resource_Policy_%28CORP%29), |
| which prevents or allows responses from being requested cross-origin, more |
| explicitly than CORB. |
| |
| Protection techniques: |
| - Enforcing Cross-Origin-Resource-Policy in the NetworkService process |
| (i.e. before the HTTP response is handed out to the renderer process). |
| - Preventing spoofing of `network::ResourceRequest::request_initiator` |
| by comparing against `request_initiator_origin_lock` in |
| `network::CorsURLLoaderFactory::IsValidRequest`. |
| |
| |
| ## Frame-ancestors CSP and X-Frame-Options response headers |
| |
| A compromised renderer shouldn’t be able to bypass `X-Frame-Options` |
| or `frame-ancestors` CSP. |
| |
| For example, if example.com/page.html sends a `X-Frame-Options: deny` header, |
| then it should never commit in a subframe, even if some renderers have |
| been compromised. |
| |
| Protection techniques: |
| - `X-Frame-Options: deny` is enforced in the browser process |
| via `content::AncestorThrottle`, an implementation of |
| `content::NavigationThrottle`. |
| - `frame-ancestors` is enforced in a renderer process, but |
| this process is considered trustworthy in this scenario |
| (because it hosts the frame that is requesting protection). |
| See also https://crbug.com/759184 which tracks |
| moving this enforcement into the browser process. |
| |
| |
| ## HTTP request headers |
| |
| Compromised renderers shouldn’t be able to control security sensitive HTTP |
| request headers like `Host`, `Origin`, or `Sec-Fetch-Site`. |
| |
| Protection techniques: |
| - Using `AreRequestHeadersSafe` to reject `Host` and other headers that |
| should only be generated internally within the NetworkService. |
| - Preventing spoofing of `network::ResourceRequest::request_initiator` |
| by comparing against `request_initiator_origin_lock` in |
| `network::CorsURLLoaderFactory::IsValidRequest`. |
| |
| |
| ## (WIP) SameSite cookies |
| |
| Compromised renderers shouldn’t be able to send a cross-site HTTP request with |
| SameSite cookies. |
| |
| **Work-in-progress / not protected today**. |
| |
| TODO(morlovich): Add details. I assume that this requires trustworthy |
| |request_initiator| (similar to the `Origin` header), but probably more |
| than that. |
| |
| See also https://crbug.com/927967. |
| |
| |
| ## (WIP) User gestures / activations. |
| |
| Compromised renderers shouldn't be able to spoof user gestures to perform |
| actions requiring them: |
| |
| - A compromised renderer should not be able to forge a gesture that affects |
| the trusted browser UI. For example, a compromised renderer should not be |
| able to interact with the Omnibox or the WebBluetooth chooser. |
| |
| - A compromised renderer should not be able to forge a gesture that grants |
| extra capabilities to a web origin. For example, a compromised renderer |
| should not be able to open an unlimited number of popup |
| windows by forging user gestures. |
| **Work-in-progress / not protected today** - see https://crbug.com/848778. |
| |
| |
| ## Web Accessible Resources of Chrome Extensions |
| |
| Compromised non-extension renderers shouldn’t be able to access |
| non-web-accessible-resources of a Chrome Extension. |
| |
| Protection techniques: |
| - Navigations: Enforcement in the browser process |
| via `extensions::ExtensionNavigationThrottle`, an implementation of |
| `content::NavigationThrottle`. This relies on non-spoofability |
| of `content::NavigationHandle::GetInitiatorOrigin`. |
| - Subresources: Enforcement in the browser process via |
| `ExtensionURLLoaderFactory::CreateLoaderAndStart`. This relies |
| on process boundaries and therefore doesn't rely on non-spoofability |
| of `network::ResourceRequest::request_initiator`. |
| |
| |
| ## Non-Web resources |
| |
| Compromised *web* renderer processes shouldn’t be able to access |
| *local* resources (e.g. `file://...` or `chrome://settings`). |
| |
| Protection techniques: |
| - TODO(lukasza, nasko): need to research |
| |
| |
| ## Android-specific protection gaps |
| |
| Due to resource constraints, on Android platforms only some sites get a |
| dedicated renderer process, isolated from other sites. |
| (Current heuristic is to isolate the sites where the user has entered a password |
| in the past.) |
| This means that some sites are hosted in a renderer process that is |
| *not* locked to any particular site. If an attacker compromises |
| an unlocked renderer process, they may try to abuse protection gaps listed |
| below. |
| |
| **Known gaps in protection**: |
| - When `CanAccessDataForOrigin` runs on the IO thread, it cannot protect |
| isolated sites against being accessed from an unlocked renderer process. |
| Some web storage protections depend on `CanAccessDataForOrigin` calls |
| on the IO thread. |
| See also https://crbug.com/764958. |
| |
| |
| ## Renderer processes hosting DevTools frontend |
| |
| If an attacker could take control over the DevTools frontend then the attacker |
| would gain access to all the cookies, storage, etc. of any origin within the |
| page and would be able to execute arbitrary scripts in any frame of the page. |
| This means that treating the DevTools renderer as untrustworthy wouldn't in |
| practice offer additional protection for the same-origin-policy. |
| |
| Because of the above: |
| |
| - Chrome ensures that the DevTools frontend is always hosted in a renderer |
| process separate from renderers hosting web origins. |
| - Chrome assumes that the DevTools frontend is always trustworthy |
| (i.e. never compromised, or under direct control of an attacker). |
| For example, when the DevTools process asks to initiate a HTTP request on |
| behalf of https://example.com, the browser process trusts the DevTools |
| renderer to claim authority to initiate requests of behalf of this origin |
| (e.g. attach SameSite cookies, send appropriate Sec-Fetch-Site request header, |
| etc.). |