| # Autofill across iframes |
| |
| Chrome Autofill fills in frame-transcending forms like the following pseudo-code |
| example. |
| |
| ``` |
| <!-- Top-level document URL: https://merchant.example/... --> |
| <form> |
| Cardholder name: <input id="name"> |
| Credit card number: <iframe src="https://psp.example/..." allow="shared-autofill"><input id="num"></iframe> |
| Expiration date: <input id="exp"> |
| CVC: <iframe src="https://psp.example/..." allow="shared-autofill"><input id="cvc"></iframe> |
| <iframe src="https://ads.example/..."><input id="account"></iframe> |
| </form> |
| ``` |
| |
| This applies to address and payment information, but not to passwords. |
| |
| ## The security policy |
| |
| An autofill fills a form control *candidate* only if one of the following is true: |
| |
| - the autofill's origin and the *candidate*'s origin are the [same origin]; |
| - [shared-autofill] is enabled in the *candidate*'s [node document] and one of |
| the following is true: |
| - the autofill's origin and the top-level origin are the [same origin]; |
| - the candidate's origin and the top-level origin are the [same origin] and the |
| *candidate*'s autofill value is non-sensitive. |
| |
| The terminology used above is defined in the [appendix](#appendix-terminology). |
| |
| This policy is the [eligibility for autofill] definition plus the additional |
| "... and one of the following is true" conjunct in the [shared-autofill] clause. |
| |
| The policy is implemented in [FormForest::GetRendererFormsOfBrowserForm()]. |
| |
| ## The rationale |
| |
| The example form above exhibits a common pattern: at the time of writing, about |
| 20% of the payment forms on the web span multiple origins. Most commonly, the |
| cardholder name field's origin is the top-level origin, whereas the credit card |
| number is in a cross-origin iframe hosted by the payment service provider (PSP). |
| |
| These iframes are typically styled so that they seamlessly integrate with the |
| merchant's page -- the user is not made aware that multiple frames and origins |
| are involved. Yet the different origins isolate the payment information from the |
| merchant's website, which helps them comply with the payment card industry's |
| data security standard (see Section 2.2.3 of the [PCI-DSS best practices]). |
| |
| Chrome Autofill's objective is to fill fields that the user expects to be |
| filled, even if those fields cross origins, while protecting the user against |
| possibly malicious sub-frames. Intuitively, we support two "directions": |
| |
| - "Downwards": An autofill may fill fields in descendant documents where |
| [shared-autofill] is enabled. In our example, an autofill initiated on the |
| cardholder name field may fill the credit card number field. |
| - "Upwards": An autofill may fill certain values in ancestor documents. In our |
| example, an autofill initiated on the credit card number field may fill the |
| cardholder name field. |
| |
| We restrict the values that may be filled "upwards" especially to prevent |
| leaking sensitive payment information -- credit card numbers and CVCs that the |
| PCI-DSS intends to protect -- into the merchant's page. The "non-sensitive" |
| values that we allow to be filled "upwards" are credit card types, cardholder |
| names, and expiration dates. |
| |
| The terms "upwards" and "downwards" are imprecise: our security policy doesn't |
| refer to the [top-level traversable]'s [node document], but rather to its |
| [origin], the top-level origin. This way, Autofill works the same when, for example, |
| the cardholder name is hosted in a same-origin iframe: `<iframe |
| src="https://merchant.example/..."><input id="name"></iframe>`. |
| |
| Our security policy does not allow "upwards" or "downwards" filling to and from |
| arbitrary documents. It only allows filling "upwards to" main-origin documents |
| and "downwards from" main-origin documents. This simplifies reasoning about the |
| security policy as well as the implementation, and is still sufficient for |
| real-world payment forms. |
| |
| The following table illustrates which fields may be filled in our example form |
| depending on the autofill's origin: |
| |
| | Autofill's origin | `name` | `num` | `exp` | `cvc` | `account` | |
| |----------------------------|:--------:|:--------:|:--------:|:--------:|:---------:| |
| | `https://merchant.example` | ✔ | ✔ | ✔ | ✔ | ✖ | |
| | `https://psp.example` | ✔ | ✔ | ✔ | ✔ | ✖ | |
| | `https://ads.example` | ✔ | ✖ | ✔ | ✖ | ✔ | |
| |
| ## Appendix: Terminology |
| |
| An *autofill* is an operation that fills one or many form control elements in |
| the [fully active descendants of a top-level traversable with user attention]. |
| An autofill can only be initiated on a [focused] form control element. |
| |
| An autofill *fills a form control* if it changes the form control's [value]. The |
| value after the autofill is the *form control's autofill value*. |
| |
| A form control's autofill value is *non-sensitive* if it is a credit card type, |
| a cardholder name, or a credit card expiration date. |
| |
| A *form control's origin* is its [node document]'s [origin]. |
| An *autofill's origin* is the [focused] form control's origin. |
| The *top-level origin* is the [top-level traversable]'s [active document]'s [origin]. |
| |
| *[shared-autofill] is enabled in a document* if the [Is feature enabled in |
| document for origin?] algorithm on [shared-autofill], the document, and the |
| document's [origin] returns `Enabled`. |
| |
| *TODO*: Update link to [eligibility for autofill] once the |
| [PR](https://github.com/whatwg/html/pull/8801) is closed. |
| |
| [FormForest::GetRendererFormsOfBrowserForm()]: https://source.chromium.org/chromium/chromium/src/+/main:components/autofill/content/browser/form_forest.cc;l=618-623;drc=94fbbc584c5d42f0097a9cb28b355853d2b34658 |
| [active document]: https://html.spec.whatwg.org/#nav-document |
| [eligibility for autofill]: https://schwering.github.io/html/#eligible-for-autofill |
| [Is feature enabled in document for origin?]: https://w3c.github.io/webappsec-permissions-policy/#algo-is-feature-enabled |
| [focused]: https://html.spec.whatwg.org/#focused |
| [fully active descendants of a top-level traversable with user attention]: https://html.spec.whatwg.org/#fully-active-descendant-of-a-top-level-traversable-with-user-attention |
| [same origin]: https://html.spec.whatwg.org/multipage/browsers.html#same-origin |
| [node document]: https://dom.spec.whatwg.org/#concept-node-document |
| [origin]: https://dom.spec.whatwg.org/#concept-document-origin |
| [PCI-DSS best practices]: https://www.pcisecuritystandards.org/ |
| [shared-autofill]: https://schwering.github.io/shared-autofill/ |
| [top-level traversable]: https://html.spec.whatwg.org/#top-level-traversable |
| [value]: https://html.spec.whatwg.org/#concept-fe-value |