Skip to content

Commit

Permalink
Document reasons for origin limitations on iframes loaded into AMP do…
Browse files Browse the repository at this point in the history
…cuments.
  • Loading branch information
cramforce committed Jan 18, 2016
1 parent 9e7bfec commit b721b8a
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 12 deletions.
2 changes: 1 addition & 1 deletion builtins/amp-ad.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ To enable this, copy the file [remote.html](../3p/remote.html) to your web serve
<meta name="amp-3p-iframe-src" content="https://assets.your-domain.com/path/to/remote.html">
```

The `content` attribute of the meta tag is the absolute URL to your copy of the remote.html file on your web server. This URL must use a "https" schema. It is not allowed to reside on the same origin as your AMP files. E.g. if you host AMP files on "www.example.com", this URL must not be on "www.example.com" but e.g. "something-else.example.com" is OK. The reason for this limitation is that AMP documents may be served by AMP caches on different origins. If the iframe is never on the same origin as the parent frame, it becomes less likely that one accidentally deploys code that relies on them being same origin, which would break in the 'AMP cache" scenario.
The `content` attribute of the meta tag is the absolute URL to your copy of the remote.html file on your web server. This URL must use a "https" schema. It is not allowed to reside on the same origin as your AMP files. E.g. if you host AMP files on "www.example.com", this URL must not be on "www.example.com" but e.g. "something-else.example.com" is OK. See the doc ["Iframe origin policy"](../spec/amp-iframe-origin-policy.md) for further details on allowed origins for iframes.

##### Enhance incoming ad configuration

Expand Down
3 changes: 2 additions & 1 deletion extensions/amp-iframe/0.1/amp-iframe.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ export class AmpIframe extends AMP.BaseElement {
!((' ' + sandbox + ' ').match(/\s+allow-same-origin\s+/i)) ||
(url.origin != containerUrl.origin && url.protocol != 'data:'),
'Origin of <amp-iframe> must not be equal to container %s' +
'if allow-same-origin is set.',
'if allow-same-origin is set. See https://github.com/ampproject/' +
'amphtml/blob/master/spec/amp-iframe-origin-policy.md for details.',
this.element);
return src;
}
Expand Down
5 changes: 5 additions & 0 deletions extensions/amp-iframe/0.1/test/test-amp-iframe.js
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,11 @@ describe('amp-iframe', () => {
'allow-same-origin');
}).to.throw(/must not be equal to container/);

expect(() => {
amp.assertSource('https://google.com/fpp', 'https://google.com/abc',
'Allow-same-origin');
}).to.throw(/must not be equal to container/);

expect(() => {
amp.assertSource('https://google.com/fpp', 'https://google.com/abc',
'allow-same-origin allow-scripts');
Expand Down
22 changes: 13 additions & 9 deletions extensions/amp-iframe/amp-iframe.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ Displays an iframe.
- `amp-iframe` may not appear close to the top of the document (except for iframes that use `placeholder` as described below). They must be either 600px away from the top or not within the first 75% of the viewport when scrolled to the top – whichever is smaller. NOTE: We are currently looking for feedback as to how well this restriction works in practice.
- They are sandboxed by default. [Details](#sandbox)
- They must only request resources via HTTPS or from a data-URI or via the srcdoc attribute.
- They must not be in the same origin as the container unless they do not allow `allow-same-origin` in the sandbox attribute.
- They must not be in the same origin as the container unless they do not allow `allow-same-origin` in the sandbox attribute. See the doc ["Iframe origin policy"](../../../spec/amp-iframe-origin-policy.md) for further details on allowed origins for iframes.

Example:
```html
<amp-iframe width=300 height=300
sandbox="allow-scripts"
sandbox="allow-scripts allow-same-origin"
layout="responsive"
frameborder="0"
src="https://foo.com/iframe">
Expand All @@ -43,7 +43,11 @@ The attributes above should all behave like they do on standard iframes.

##### sandbox

Iframes created by `amp-iframe` always have the `sandbox` attribute defined on them. By default the value is empty. That means that they are "maximum sandboxed" by default. By setting sandbox values, one can opt the iframe into being less sandboxed. All values supported by browsers are allowed. E.g. setting `sandbox="allow-scripts"` allows the iframe to run JavaScript, or `sandbox="allow-popups allow-popups"` allows the iframe to run JavaScript and open new windows.
Iframes created by `amp-iframe` always have the `sandbox` attribute defined on them. By default the value is empty. That means that they are "maximum sandboxed" by default. By setting sandbox values, one can opt the iframe into being less sandboxed. All values supported by browsers are allowed. E.g. setting `sandbox="allow-scripts"` allows the iframe to run JavaScript, or `sandbox="allow-scripts allow-same-origin"` allows the iframe to run JavaScript, make non-CORS XHRs, and read/write cookies.

If you are iframing a document that was not specifically created with sandboxing in mind, you will most likely need to add `allow-scripts allow-same-origin` to the `sandbox` attribute and you mights need to allow additional capabilities.

Note also, that the sandbox applies to all windows opened from a sandboxed iframe. This includes new windows created by a link with `target=_blank` (Add `allow-popups` to allow this to happen). Adding `allow-popups-to-escape-sandbox` to the `sandbox` attribute, makes those new windows behave like non-sandboxed new windows. This is likely most of the time what you want and expect. Unfortunately, as of this writing, `allow-popups-to-escape-sandbox` is only supported by Chrome.

See the [the docs on MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-sandbox) for further details on the sandbox attribute.

Expand All @@ -62,7 +66,7 @@ Example of `amp-iframe` with `overflow` element:
```html
<amp-iframe width=300 height=300
layout="responsive"
sandbox="allow-scripts"
sandbox="allow-scripts allow-same-origin"
resizable
src="https://foo.com/iframe">
<div overflow tabindex=0 role=button aria-label="Read more">Read more!</div>
Expand All @@ -71,10 +75,10 @@ Example of `amp-iframe` with `overflow` element:

Example of IFrame resize request:
```javascript
window.parent./*OK*/postMessage({
window.parent.postMessage({
sentinel: 'amp',
type: 'embed-size',
height: document.body./*OK*/scrollHeight
height: document.body.scrollHeight
}, '*');
```

Expand All @@ -96,17 +100,17 @@ It is possible to have an `amp-iframe` appear on the top of a document when the
```html
<amp-iframe width=300 height=300
layout="responsive"
sandbox="allow-scripts"
sandbox="allow-scripts allow-same-origin"
src="https://foo.com/iframe">
<amp-img layout="fill" src="https://foo.com/foo.png" placeholder></amp-img>
</amp-iframe>
```
- The `amp-iframe` must contain an element with the `placeholder` attribute, (for instance an `amp-img` element) which would be rendered as a placeholder till the iframe is ready to be displayed.
- Iframe readiness can be known by listening to `onload` of the iframe or an `embed-ready` postmesssage which would be sent by the Iframe document, whichever comes first.
- Iframe readiness can be known by listening to `onload` of the iframe or an `embed-ready` postMessage which would be sent by the Iframe document, whichever comes first.

Example of IFrame embed-ready request:
```javascript
window.parent./*OK*/postMessage({
window.parent.postMessage({
sentinel: 'amp',
type: 'embed-ready'
}, '*');
Expand Down
15 changes: 15 additions & 0 deletions spec/amp-iframe-origin-policy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Iframe origin policy

Various AMP features allow loading iframes from arbitrary origins into AMP pages. Examples are the [`amp-iframe`](../extensions/amp-iframe/amp-iframe.md) element and [the custom domain feature of `amp-ad`](../builtins/amp-ad.md#running-ads-from-a-custom-domain). The origin of a URL such as `https://example.com/some/path` is `https://example.com`. See [the HTML5 spec](https://www.w3.org/TR/2011/WD-html5-20110525/origin-0.html#origin) for details.

These iframes are typically allowed to execute arbitrary JavaScript, but for security reasons they are never allowed to access the AMP document itself using any method besides sending messages via postMessage.

AMP documents are designed to be accessible both through the web servers and origins where they are hosted and AMP proxy caches (such as cdn.ampproject.org). In the latter case, the iframes would never be on the same origin as the document, because iframes cannot be hosted on the proxy cache. This enforces the security rule from above.

In the case where iframe and document are hosted by the same party the rule is much less relevant, because the cross iframe access would happen exclusively between HTML pages owned by the same party. Having said that, AMP wants to ensure that AMP documents behave the same whether they are served through a proxy or served from the origin domain. If iframe and document were on the same origin in the latter case, one could accidentally write code that relies on them being on the same origin. Such documents would then break when hosted on the cache. To ensure that direct JavaScript access between AMP document and iframe is never possible, AMP thus enforces that they are not on the same origin. There is one exception to this. `amp-iframe` uses a restrictive [iframe-sandbox](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-sandbox) by default. If one does not opt into `allow-same-origin`, then every origin is allowed for the iframe. As soon as you add `allow-same-origin` to the sandbox the origin rules apply.

In concrete terms this means: If your main site is hosted on `www.example.com`, then you cannot include an iframe from `www.example.com`. Every other origin such as `iframe.example.com` or `assets.example.com` is fine.

## Security impact

The above only enforces that documents do not rely on cross-frame access for functionality. There is no guarantee that iframes are never on the same origin as the origin an AMP document is hosted on. One can easily circumvent AMP's not-same-origin-enforcement through redirects, since only the initial URL is tested.
3 changes: 2 additions & 1 deletion src/3p-frame.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ function getCustomBootstrapBaseUrl(parentWindow) {
// redirect to the proxy origin which is the important one.
assert(parseUrl(url).origin != parseUrl(parentWindow.location.href).origin,
'3p iframe url must not be on the same origin as the current document ' +
'%s in element %s.', url, meta);
'%s in element %s. See https://github.com/ampproject/amphtml/blob/' +
'master/spec/amp-iframe-origin-policy.md for details.', url, meta);
return url + '?$internalRuntimeVersion$';
}

0 comments on commit b721b8a

Please sign in to comment.