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.

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.

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 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:

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) 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_corb_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:

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 and content script security guidance.
  • Spoof the id of a Chrome extension initiating native messaging 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:

  • Validating origins sent in IPCs from a renderer process by using CanAccessDataForOrigin in CodeCacheHostImpl::DidGenerateCacheableMetadataInCacheStorage.
  • 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), 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.).