Skip to content

Commit

Permalink
Pick up, dust off, and improve cookie layering
Browse files Browse the repository at this point in the history
This patch does the following to the work in whatwg#1707:
- rebase to main
- add logic for parsing and storing cookies
- point to the IETF-hosted draft cookie spec
- don't point to storage access API for has storage access, use a broken
  link instead
- add a broken link to environment/ancestry
- add a broken link for the request's initiator origin plumbed in from HTML. It'll be defined here, but we need to modify HTML so we can track it in the top.
- add broken links to things that need to be added to HTML
- fix some nits (e.g. "foo" -> "<code>foo</code>")
- use [=secure context=] not scheme=https
- use SameSite=None by default. Let's punt on that for now, given the
  current state of implementations and lack of clear path forward.
  • Loading branch information
bvandersloot-mozilla committed Jan 28, 2025
1 parent 07662d3 commit 5971b14
Showing 1 changed file with 144 additions and 33 deletions.
177 changes: 144 additions & 33 deletions fetch.bs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,29 @@ urlPrefix:https://tc39.es/ecma262/#;type:dfn;spec:ecma-262
url:realm;text:realm
url:sec-list-and-record-specification-type;text:Record
url:current-realm;text:current realm

urlPrefix:https://www.ietf.org/archive/id/draft-annevk-johannhof-httpbis-cookies-00.html#;type:dfn;spec:cookies
url:name-cookie-store-and-limits;text:cookie store
url:name-parse-and-store-a-cookie;text:parse and store a cookie
url:name-parse-a-cookie;text:parse a cookie
url:name-store-a-cookie;text:store a cookie
url:name-retrieve-cookies;text:retrieve cookies
url:name-serialize-cookies;text:serialize cookies

<!-- TODO: pending HTML changes- ancestor enum (https://github.com/whatwg/html/pull/10559), has storage access bit, initiator origin plumbing -->
urlPrefix:https://html.spec.whatwg.org#;type:dfn;spec:html
url:TODO;text:ancestry;for:environment
url:TODO;text:has storage access;for:environment
url:TODO;text:initiator origin;for:request
</pre>

<pre class=biblio>
{
"COOKIES": {
"authors": ["Johann Hofmann", "Anne Van Kesteren"],
"href": "https://www.ietf.org/archive/id/draft-annevk-johannhof-httpbis-cookies-00.html",
"title": "Cookies: HTTP State Management Mechanism"
},
"HTTP": {
"aliasOf": "RFC9110"
},
Expand Down Expand Up @@ -2226,31 +2245,38 @@ or "<code>object</code>".
<hr>

<div algorithm>
<p>A <a for=/>request</a> <var>request</var> has a
<dfn for=request id=concept-request-tainted-origin>redirect-tainted origin</dfn> if these steps
return true:
<p>A <a for=/>request</a> has a <dfn for=request id=concept-request-redirect-taint>redirect-taint</dfn>,
which is "<code>None</code>", "<code>Cross-Origin</code>", or "<code>Cross-Site</code>".
<p>To get <a for=/>request</a> <var>request</var>'s <a>redirect-taint</a>:

<ol>
<li><p><a for=/>Assert</a>: <var>request</var>'s <a for=request>origin</a> is not
"<code>client</code>".

<li><p>Let <var>lastURL</var> be null.

<li><p>Let <var>crossOriginTaint</var> be "<code>None</code>".

<li>
<p><a for=list>For each</a> <var>url</var> of <var>request</var>'s <a for=request>URL list</a>:

<ol>
<li><p>If <var>lastURL</var> is null, then set <var>lastURL</var> to <var>url</var> and
<a for=iteration>continue</a>.

<li><p>If <var>url</var>'s <a for=url>origin</a> is not <a for=/>same site</a> with
<var>lastURL</var>'s <a for=url>origin</a> and <var>request</var>'s <a for=request>origin</a> is
not <a for=/>same site</a> with <var>lastURL</var>'s <a for=url>origin</a>, then return "<code>Cross-Site</code>".

<li><p>If <var>url</var>'s <a for=url>origin</a> is not <a>same origin</a> with
<var>lastURL</var>'s <a for=url>origin</a> and <var>request</var>'s <a for=request>origin</a> is
not <a>same origin</a> with <var>lastURL</var>'s <a for=url>origin</a>, then return true.
not <a>same origin</a> with <var>lastURL</var>'s <a for=url>origin</a>,
then let <var>crossOriginTaint</var> be "<code>Cross-Origin</code>"..

<li>Set <var>lastURL</var> to <var>url</var>.
</ol>

<li>Return false.
<li>Return <var>crossOriginTaint</var>.
</ol>
</div>

Expand All @@ -2262,8 +2288,8 @@ run these steps:
<li><p><a for=/>Assert</a>: <var>request</var>'s <a for=request>origin</a> is not
"<code>client</code>".

<li><p>If <var>request</var> has a <a for=request>redirect-tainted origin</a>, then return
"<code>null</code>".
<li><p>If <var>request</var>'s <a for=request>redirect-taint</a> is not "<code>None</code>",
then return "<code>null</code>".

<li><p>Return <var>request</var>'s <a for=request>origin</a>,
<a lt="ASCII serialization of an origin">serialized</a>.
Expand Down Expand Up @@ -2372,8 +2398,8 @@ source of security bugs. Please seek security review for features that deal with
"<a for="embedder policy value"><code>credentialless</code></a>", then return true.</p>

<li><p>If <var>request</var>'s <a for=request>origin</a> is <a>same origin</a> with
<var>request</var>'s <a for=request>current URL</a>'s <a for=url>origin</a> and <var>request</var>
does not have a <a for=request>redirect-tainted origin</a>, then return true.</p>
<var>request</var>'s <a for=request>current URL</a>'s <a for=url>origin</a> and <var>request</var>'s
<a for=request>redirect-taint</a> is not "<code>None</code>", then return true.</p>

<li><p>Return false.</p>
</ol>
Expand Down Expand Up @@ -2489,6 +2515,9 @@ this is also tracked internally using the request's <a for=request>timing allow
<p>A <a for=/>response</a> has an associated <dfn for=response>has-cross-origin-redirects</dfn>
(a boolean), which is initially false.

<p>A <a for=/>response</a> has an associated <dfn for=response>has-cross-site-redirects</dfn>
(a boolean), which is initially false.

<hr>

<p>A <dfn export id=concept-network-error>network error</dfn> is a <a for=/>response</a> whose
Expand Down Expand Up @@ -2710,7 +2739,6 @@ manually. [[!HTML]]
</ol>
</div>


<h3 id=authentication-entries>Authentication entries</h3>

<p>An <dfn export>authentication entry</dfn> and a <dfn export>proxy-authentication entry</dfn> are
Expand Down Expand Up @@ -3292,6 +3320,105 @@ through TLS using ALPN. The protocol cannot be spoofed through HTTP requests in

<h2 id=http-extensions>HTTP extensions</h2>

<h3 id=cookie-header>`<code>Cookie</code>` header</h3>

<p>The `<dfn export http-header id=http-cookie><code>Cookie</code></dfn>`
request <a for=/>header</a> allows the request to carry locally stored state, such as user credentials.

<div algorithm>
<p>To <dfn id=append-a-request-cookie-header>append a request `<code>Cookie</code>` header</dfn>,
given a <a for=/>request</a> <var>request</var>, run these steps:
<ol>
<li><p>Let |sameSite| be the result of [=determining the same-site mode=] for <var>request</var>.
<li><p>Let |isSecure| be false.
<li><p>If <var>request</var>'s <a for=request>client</a> is a <a>secure context</a>, then set |isSecure| to true.
<li><p>Let |httpOnlyAllowed| be true.
<p class=note>Fetch implies that the request is http-only, as opposed to document.cookie
<li><p>Let |partitionKey| be the result of [=computing the cookie partition key=] for <var>request</var>.
<li><p>Let |partitionedContext| be the result of [=determining the partitioned context state=] for |request|.
<li><p>Let |cookies| be the result of running <a>retrieve cookies</a> given
|isSecure|,
<var>request</var>'s <a for=request>current URL</a>'s <a for=url>host</a>,
<var>request</var>'s <a for=request>current URL</a>'s <a for=url>path</a>,
|httpOnlyAllowed|,
|sameSite|,
|partitionKey|
and |partitionedContext|.

<p class=note>It is expected that the cookie store returns an ordered list of cookies
<li>If |cookies| <a for="list">is empty</a>, then return.
<li>Let |value| be the result of running <a>serialize cookies</a> given |cookies|.
<li><a for="header list">Append</a> (`<code>Cookie</code>`, <var>value</var>) to <var>request</var>'s <a for=request>header list</a>.
</ol>
</div>

<div algorithm>
<p>To <dfn id=parse-and-store-response-cookie-headers>parse and store response `<code>Set-Cookie</code>` headers</dfn>,
given a <a for=/>request</a> <var>request</var> and a <a for=/>response</a> <var>response</var>, run these steps:
<ol>
<li><p>Let |allowNonHostOnlyCookieForPublicSuffix| be false.
<li><p>Let |isSecure| be false.
<li><p>If <var>request</var>'s <a for=request>client</a> is a <a>secure context</a>, then set |isSecure| to true.
<li><p>Let |httpOnlyAllowed| be true.
<p class=note>Fetch implies that the request is http-only, as opposed to document.cookie
<li><p>Let |partitionKey| be the result of [=computing the cookie partition key=] for <var>request</var>.
<li><p>Let |partitionedContext| be the result of [=determining the partitioned context state=] for |request|.
<li><p>Let |sameSiteStrictOrLaxAllowed| be true if the result of [=determine the same-site mode=] for |request| is "<code>StrictOrLess</code>", and false otherwise.
<li><p><a for=list>For each</a> <var>header</var> of <var>response</var>'s <a for=response>header list</a>:
<ol>
<li><p>If <var>header</var>'s <a for=header>name</a> is not a <a>byte-case-insensitive</a> match for `<code>Set-Cookie</code>`, then <a for=iteration>continue</a>.
<li><p><a>Parse and store a cookie</a> given
<var>header</var>'s <a for=header>value</a>,
|isSecure|,
<var>request</var>'s <a for=request>current URL</a>'s <a for=url>host</a>,
<var>request</var>'s <a for=request>current URL</a>'s <a for=url>path</a>,
|httpOnlyAllowed|,
|allowNonHostOnlyCookieForPublicSuffix|,
|sameSiteStrictOrLaxAllowed|,
|partitionKey|
and |partitionedContext|.
</ol>
</ol>
</div>

<div algorithm>
<p>To <dfn>determine the same-site mode</dfn> for a given <a for=/>request</a> <var>request</var>, run these steps:
<ol>
<li><p><a for=/>Assert</a>: <var>request</var>'s <a for=request>method</a> is "GET" or "POST".
<li><p>If <var>request</var>'s <a for=request>method</a> is "GET" and
<var>request</var>'s <a for=request>destination</a> is "document", return "<code>LaxOrLess</code>".
<li><p>If <var>request</var>'s <a for=request>client</a>'s <a for=environment>ancestry</a> is "<code>cross-site</code>", return "<code>UnsetOrLess</code>".
<li><p>If <var>request</var>'s <a for=request>redirect-taint</a> is "<code>Cross-Site</code>", return "<code>UnsetOrLess</code>".
<li><p>If <var>request</var>'s <a for=request>initiator origin</a> is not <a for=/>same site</a> to <var>request</var>'s <a for=request>URL</a>'s <a for=url>origin</a>, return "<code>UnsetOrLess</code>".
<li><p>Return "StrictOrLess".
</ol>
</div>


<div algorithm>
<p>To <dfn>compute the cookie partition key</dfn> for a given <a for=/>request</a> <var>request</var>, run these steps:
<ol>
<li><p>Let <var>topLevelOrigin</var> be <var>request</var>'s <a for=request>client</a>'s
<a for="environment">top-level origin</a>.

<li><p>Let <var>topLevelSite</var> be the result of <a lt="obtain a site">obtaining a site</a>,
given <var>topLevelOrigin</var>.

<li><p>Let <var>crossSiteAncestors</var> be true if <var>request</var>'s <a for=request>client</a>'s <a for=environment>ancestry</a> is "<code>cross-site</code>", false otherwise.

<li><p>Return (<var>topLevelSite</var>, <var>crossSiteAncestors</var>).
</ol>
</div>

<div algorithm>
<p>To <dfn>determine the partitioned context state</dfn> for a given <a for=/>request</a> <var>request</var>, run these steps:
<ol>
<li><p>If <var>request</var>'s <a for=request>client</a>'s <a for=environment>ancestry</a> is not "<code>cross-site</code>", return false.
<li><p>If <var>request</var>'s <a for=request>client</a>'s [=environment/has storage access=] is true, return false.
<li><p>Return true.
</ol>
</div>

<h3 id=origin-header>`<code>Origin</code>` header</h3>

<p>The `<dfn export http-header id=http-origin><code>Origin</code></dfn>`
Expand Down Expand Up @@ -4680,9 +4807,12 @@ steps:
<!-- If you are ever tempted to move this around, carefully consider responses from about URLs,
blob URLs, service workers, HTTP cache, HTTP network, etc. -->

<li><p>If <var>request</var> has a <a for=request>redirect-tainted origin</a>, then set
<li><p>If <var>request</var>'s <a for=request>redirect-taint</a> is not "<code>None</code>", then set
<var>internalResponse</var>'s <a for=response>has-cross-origin-redirects</a> to true.

<li><p>If <var>request</var>'s <a for=request>redirect-taint</a> is "<code>Cross-Site</code>", then set
<var>internalResponse</var>'s <a for=response>has-cross-site-redirects</a> to true.

<li><p>If <var>request</var>'s <a for=request>timing allow failed flag</a> is unset, then set
<var>internalResponse</var>'s <a for=response>timing allow passed flag</a>.

Expand Down Expand Up @@ -5710,21 +5840,9 @@ run these steps:
<p>If <var>includeCredentials</var> is true, then:

<ol>
<li>
<p>If the user agent is not configured to block cookies for <var>httpRequest</var> (see
<a href=https://httpwg.org/specs/rfc6265.html#privacy-considerations>section 7</a> of
[[!COOKIES]]), then:

<ol>
<li><p>Let <var>cookies</var> be the result of running the "cookie-string" algorithm (see
<a href=https://httpwg.org/specs/rfc6265.html#cookie>section 5.4</a> of
[[!COOKIES]]) with the user agent's cookie store and <var>httpRequest</var>'s
<a for=request>current URL</a>.
<p class=note>This permits some implementations to choose to not support cookies for some or all <var>httpRequest</var>s.

<li>If <var>cookies</var> is not the empty string, then <a for="header list">append</a>
(`<code>Cookie</code>`, <var>cookies</var>) to <var>httpRequest</var>'s
<a for=request>header list</a>.
</ol>
<li><p>The user agent should <a>append a request `<code>Cookie</code>` header</a> for <var>httpRequest</var>.

<li>
<p>If <var>httpRequest</var>'s <a for=request>header list</a>
Expand Down Expand Up @@ -6288,14 +6406,7 @@ optional boolean <var>forceNewConnection</var> (default false), run these steps:
<li><p>Set <var>response</var>'s <a for=response>body</a> to a new <a for=/>body</a> whose
<a for=body>stream</a> is <var>stream</var>.

<li><p tracking-vector>If <var>includeCredentials</var> is true and the user agent is not
configured to block cookies for <var>request</var> (see
<a href=https://httpwg.org/specs/rfc6265.html#privacy-considerations>section 7</a> of
[[!COOKIES]]), then run the "set-cookie-string" parsing algorithm (see
<a href=https://httpwg.org/specs/rfc6265.html#set-cookie>section 5.2</a> of [[!COOKIES]]) on the
<a for=header>value</a> of each <a for=/>header</a> whose <a for=header>name</a> is a
<a>byte-case-insensitive</a> match for `<code>Set-Cookie</code>` in <var>response</var>'s
<a for=response>header list</a>, if any, and <var>request</var>'s <a for=request>current URL</a>.
<li><p tracking-vector>If <var>includeCredentials</var> is true, the user agent should <a>parse and store response `<code>Set-Cookie</code>` headers</a> given <var>request</var> and <var>response</var>.

<li>
<p>Run these steps <a>in parallel</a>:
Expand Down

0 comments on commit 5971b14

Please sign in to comment.