chromium/docs/security/origin-vs-url.md

# Use origin (rather than URL) for security decisions.

URLs are often not sufficient for security decisions, since the origin
may not be present in the URL (e.g., `about:blank`),
may be tricky to parse (e.g., `blob:` or `filesystem:` URLs),
or may be
[opaque](https://html.spec.whatwg.org/multipage/origin.html#concept-origin-opaque)
despite a normal-looking URL (e.g., the security context may be
[sandboxed](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-sandbox)).
Use origins whenever possible.


## Use `GetLastCommittedOrigin()` or `GetSecurityContext()`.

### Good

```c++
// Example of browser-side code.
content::RenderFrameHost* frame = ...;
if (safelist.Matches(frame->GetLastCommittedOrigin()) {
  // ...
}

// Example of renderer-side code.  Note that browser-side checks are still
// needed to ensure that a compromised renderer cannot bypass renderer-side-only
// checks.
blink::Frame* frame = ...;
if (safelist.Matches(frame->GetSecurityContext()->GetSecurityOrigin()) {
  // ...
}
```

### Bad

```c++
// Example of browser-side code.
content::RenderFrameHost* frame = ...;
if (safelist.Matches(frame->GetLastCommittedURL()) {
  // BUG: doesn't work for about:blank or sandboxed frames.
}

// Example of renderer-side code.  Note that browser-side checks are still
// needed to ensure that a compromised renderer cannot bypass renderer-side-only
// checks.
blink::LocalFrame* frame = ...;
if (safelist.Matches(frame->GetDocument()->Url()) {
  // BUG: doesn't work for about:blank or sandboxed frames.
  // BUG: doesn't work for RemoteFrame(s) which don't have a local Document
  //      object and don't know the URL (only the origin) of the frame.
}
```


## Don't use the `GURL` type to store origins.

`GURL origin` is an anti-pattern - representing origins as a URL-focused data
type means that 1) some information is lost (e.g., origin's nonce and precursor
information) and 2) some unnecessary information may be present (e.g., URL path
and/or query parts are never part of an origin).

Use the following datatypes to represent origins:

- C++: `url::Origin` or `blink::SecurityOrigin`
  (instead of `GURL` or `blink::KURL`).
- Mojo: `url.mojom.Origin`
  (instead of `url.mojom.Url`).
  Remember to
  [validate data](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/security/mojo.md#Validate-privilege_presuming-data-received-over-IPC)
  received over IPC from untrustworthy processes
  or even better
  [avoid sending origins](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/security/mojo.md#Do-not-send-unnecessary-or-privilege_presuming-data)
  in the first place.
- Java: `org.chromium.url.Origin`
  (see also `url::Origin::FromJavaObject` and `url::Origin::ToJavaObject`).


## Avoid converting URLs to origins.

### Good

```c++
url::Origin origin = GetLastCommittedOrigin();
```

### Bad

```c++
GURL url = ...;
GURL origin = url.DeprecatedGetOriginAsURL();
// BUG: `origin` will be incorrect if `url` is an "about:blank" URL
// BUG: `origin` will be incorrect if `url` came from a sandboxed frame.
// BUG: `origin` will be incorrect when `url` (rather than
//      `base_url_for_data_url`) is used when working with loadDataWithBaseUrl
//      (see also https://crbug.com/1201514).
// BUG: `origin` will be empty if `url` is a blob: URL like
//      "blob:http://origin/guid-goes-here".
// NOTE: `GURL origin` is also an anti-pattern; see the "Use correct type to
//       represent origins" section below.

// Blink-specific example:
KURL url = ...;
scoped_refptr<SecurityOrigin> origin = SecurityOrigin::Create(url);
// BUG: `origin` will be incorrect if `url` is an "about:blank" URL
// BUG: `origin` will be incorrect if `url` came from a sandboxed frame.
```

### Risky

If you know what you are doing and really need to convert a URL into an origin,
then you can consider using `url::Origin::Create`, `url::SchemeHostPort`, or
`url::Origin::Resolve` (noting the limitations of those APIs - see below for
more details).

```c++
const GURL& url = ...;

// WARNING: `url::Origin::Create(url)` can give unexpected results if:
// 1) `url` is "about:blank", or "about:srcdoc"
//    (returning unique, opaque origin rather than the real origin of the frame)
// 2) `url` comes from a sandboxed frame
//    (potentially returning a non-opaque origin, when an opaque one is needed)
// 3) `base_url_for_data_url` should be used instead of `url`
//
// WARNING: `url::Origin::Create(url)` has some additional subtleties:
// 4) if `url` is a blob: or filesystem: URL like "blob:http://origin/blob-guid"
//    then the inner origin will be returned (unlike with `url::SchemeHostPort`)
// 5) data: URLs will be correctly be translated into opaque origins, but the
//    precursor origin will be lost (unlike with `url::Resolve`).
url::Origin origin = url::Origin::Create(url);

// WARNING: `url::SchemeHostPort(url)` will *mechanically* extract the scheme,
// host, and port of the URL, discarding other parts of the URL.  This may have
// unexpected results when:
// 1) `url` is "about:blank", or "about:srcdoc"
// 2) `url` comes from a sandboxed frame, i.e. when an opaque origin is expected
// 3) `url` is a data: URL, i.e. when an opaque origin is expected
// 4) `url` is a blob: or filesystem: URL like "blob:http://origin/blob-guid"
//    (the inner origin will *not* be returned - unlike `url::Origin::Create`)
url::SchemeHostPort scheme_host_port = url::SchemeHostPort(url);

// `url::Origin::Resolve` should work okay when:
// 1) `url` is "about:blank", or "about:srcdoc"
// 2) `url` comes from a sandboxed frame (i.e. when `base_origin` is opaque)
// 3) `url` is a data: URL (i.e. propagating precursor of `base_origin`)
// 4) `url` is a blob: or filesystem: URL like "blob:http://origin/blob-guid"
//
// WARNING: It is simpler and more robust to just use `GetLastCommittedOrigin`
// (instead of combining `GetLastCommittedOrigin` and `GetLastCommittedURL`
// using `url::Origin::Resolve`).
// WARNING: `url::Origin::Resolve` is unaware of `base_url_for_data_url`.
const url::Origin& base_origin = ...
url::Origin origin = url::Origin::Resolve(url, base_origin);
```