Skip to content

Commit acd996f

Browse files
author
Daniel Rubery
committed
Flesh out spec
This adds a lot more detail to the spec. It's still not fully complete and explicit, but at this point it should be a better reference than the explainer.
1 parent 0edf8f7 commit acd996f

File tree

1 file changed

+133
-40
lines changed

1 file changed

+133
-40
lines changed

spec.bs

+133-40
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ different situations. Some of the use cases of DBSC are:
9090
transaction is legitimate.
9191
</div>
9292

93-
# Security Considerations # {#privacy}
93+
# Security Considerations # {#security-considerations}
9494
The goal of DBSC is to reduce session theft by offering an alternative to
9595
long-lived cookie bearer tokens, that allows session authentication that is
9696
bound to the user's device. This makes the internet safer for users in that it
@@ -103,6 +103,9 @@ As long as the session is valid a host can know with cryptographic certainty
103103
that it is on the same device as the session was originally bound to if the
104104
session was registered to a secure device.
105105

106+
In order to ensure this, session private keys should be stored in a way
107+
that cannot be exfiltrated by locally running malware, whenever possible.
108+
106109
## Non-goals ## {#non-goals}
107110
DBSC will not prevent temporary access to the browser session while the attacker
108111
is resident on the user's device. The private key should be stored as safely as
@@ -154,17 +157,9 @@ If third party cookies are enabled it is possible for an attacker to leak
154157
whether or not a user is authenticated by measuring how long the request takes
155158
as the refresh is quite slow, partially due to the latency of TPM operations.
156159

157-
This only applies if the site to be leaked about has enabled third party
158-
cookies, if an attacker does have third-party cookie access there are often
159-
attackes and leaks.
160-
161-
This is not a very reliable leak as the user needs to have a session on the site
162-
that is currently out of date and would need to be refreshed. The leak cannot be
163-
trivially repeated as the first request will renew the session that would likely
164-
not expire again for some time.
165-
166-
It is important websites think about this privacy leak before adopting DBSC,
167-
even more so if the plan is to use sessions with third party cookies.
160+
This is mitigated by requiring user opt-in through the Storage Access
161+
API. The victim site must request access to its first-party state in
162+
order for DBSC to apply within the cross-site context.
168163

169164
# Alternatives considered # {#alternatives}
170165
## WebAuthn and silent mediation ## {#alternatives-webauthn}
@@ -206,19 +201,25 @@ A <dfn>device bound session</dfn> is a [=struct=] with the following
206201
:: a [=string=] that is to be used as the next challenge for this session
207202
: [=session scope=]
208203
:: a [=struct=] defining which [=url=]'s' are in scope for this session
209-
: [=session credential=]
210-
:: a [=list=] of [=session credential=] used by the session
204+
: [=session credentials=]
205+
:: a [=list=] of [=session credential=]s used by the session
206+
: <dfn>expiration timestamp</dfn>
207+
:: a [=moment=] when this session should be removed.
208+
: <dfn>session key</dfn>
209+
:: a key pair used by the session. The private key
210+
should be stored in a secure manner, see {#security-considerations}.
211211
</dl>
212212

213213
## Session scope ## {#framework-scope}
214214
The <dfn>session scope</dfn> is a [=struct=] with the following
215215
[=struct/items=]:
216216
<dl dfn-for="session scope">
217+
: <dfn>origin</dfn>
218+
:: The [=origin=] this session was registered for.
217219
: <dfn>include site</dfn>
218-
:: a [=boolean=] that indicates if all subdomains of are included by
219-
default.
220-
: [=scope specification=]
221-
:: a [=list=] of [=scope specification=] used by the session
220+
:: a [=boolean=] indicating if the session applies to an entire site or just an origin.
221+
: [=scope specifications=]
222+
:: a [=list=] of [=scope specification=]s used by the session
222223
</dl>
223224

224225
## Scope specification ## {#framework-scope-specification}
@@ -228,8 +229,10 @@ The <dfn>scope specification</dfn> is a [=struct=] with the following
228229
: <dfn>type</dfn>
229230
:: a [=string=] to be either "include" or "exclude", defining if the item
230231
defined in this struct should be added or removed from the scope
232+
: <dfn>host</dfn>
233+
:: a [=string=] defining the domain or domain pattern that must match for this scope specification to apply.
231234
: <dfn>path</dfn>
232-
:: a [=string=] that defines the path part of this scope item
235+
:: a [=string=] that defines the path part of this scope specification
233236
</dl>
234237

235238
## Session credential ## {#framework-session-credential}
@@ -261,16 +264,59 @@ The <dfn>session credential</dfn> is a [=struct=] with the following
261264
1. Return |domain sessions|[|session identifier|]
262265
</div>
263266

264-
## Process challenge ## {#algo-process-challenge}
265-
<div class="algorithm" data-algorithm="process-challenge">
267+
## Identify if a URL is in scope of a session ## {#algo-url-in-scope}
268+
<div class="algorithm" data-algorithm="url-in-scope">
269+
This algorithm describes how to determine if a URL is in scope of a
270+
device bound session. Given a [=url=] and [=session scope=], returns
271+
true if the [=url=] is in scope, and false otherwise.
272+
273+
1. If [=url=] is not same origin with the [=session scope=] origin and
274+
the origin is not the eTLD+1, return false.
275+
1. If the [=session scope=] origin is the eTLD+1 and the [=url=] is
276+
not same-site with the eTLD+1, return false.
277+
1. [=list/for each=] |scope specification| in the |session scope|
278+
1. If the host and path of [=url=] match the |scope specification|,
279+
return the |scope specification|'s type.
280+
1. If the [=url=] matches the |session|'s refresh URL, return false.
281+
282+
1. If [=session scope=] sets |include site|, return whether [=url=] is
283+
same-site with the scope origin.
284+
1. Return whether [=url=] is same origin with the scope origin.
285+
</div>
286+
287+
## Identify session needing refresh ## {#algo-identify-session-needing-refresh}
288+
<div class="algorithm" data-algorithm="identify-session-needing-refresh">
289+
Given a [=request=] (|request|) and [=sessions by registrable
290+
domain=], this algorithm describes how to identify which session, if
291+
any, should block |request| from proceeding.
292+
293+
1. Let |site| be the [=host/registrable domain=] of the request's URL.
294+
1. Let |domain sessions| be [=sessions by registrable domain=][site] as [=/session by id=]
295+
1. [=list/For each=] |session| of |domain sessions|
296+
1. The browser MAY skip |session| in order to prevent denial of
297+
service for the user or site. For example if |session| is
298+
requesting excessive TPM operations (harming the user) or the
299+
refresh endpoint has recently been unreachable (denial of service
300+
risk for the site).
301+
1. Run the steps in [[#algo-url-in-scope]] on the |request|'s URL
302+
and |session|'s scope.
303+
1. If the result does not indicate the request is in scope, skip |session|
304+
and continue the loop.
305+
1. If any of |session|'s credentials would be included on |request|,
306+
with the exception of Max-Age and Expires attributes, but are not
307+
present on |request|, return |session|.
308+
1. If no session has been identified, return null.
309+
</div>
310+
311+
## Cache challenge ## {#algo-cache-challenge}
312+
<div class="algorithm" data-algorithm="cache-challenge">
266313
This algorithm describes how to
267314
<dfn export dfn-for="algorithms">process a challenge</dfn> received in an HTTP
268315
header.
269316

270317
Given a [=response=] (|response|) and a [=sessions by registrable domain=], this
271318
algorithm updates the [=device bound session/cached challenge=] for a
272-
[=device bound session=], or immediately resends the [=DBSC proof=] signed with
273-
the new challenge if the [=response/status=] is 401.
319+
[=device bound session=].
274320

275321
1. Let |header name| be "<code>Sec-Session-Challenge</code>".
276322
1. Let |challenge list| be the result of executing <a>get a structured
@@ -284,8 +330,6 @@ The <dfn>session credential</dfn> is a [=struct=] with the following
284330
1. Let |session id| be null.
285331
1. If params["id"] exists and is a <a>sf-string</a>, Set |session id| to
286332
params["id"].
287-
1. If [=response/status=] is 401, resend this request as is with updated
288-
|challenge| in [=DBSC proof=] and [=iteration/continue=].
289333
1. If |session id| is null, [=iteration/continue=].
290334
1. Identify session as described in [=identify a session=] given
291335
|response| and |session id| and store as |session object|.
@@ -294,10 +338,51 @@ The <dfn>session credential</dfn> is a [=struct=] with the following
294338
[=DBSC proof=] is to be sent from this [=device bound session=].
295339
</div>
296340

297-
## Session refresh ## {#algo-session-refresh}
298-
To <dfn export id="refresh-session">Refreshing an existing session</dfn>
299-
300341
## Send request ## {#algo-session-request}
342+
<div class="algorithm" data-algorithm="session-request">
343+
This algorithm describes how to <dfn export dnf-for="algorithms">send
344+
a request</dfn> for a device bound session registration or refresh. It
345+
takes as input an |originating request|, |key pair|, |destination|, and
346+
optional |session id|, |challenge|, and |authorization|.
347+
348+
1. Let |signed challenge| be null. If |challenge| is non-null, sign it
349+
with |key pair| and store the result in |signed challenge|.
350+
1. Create a |request| for use in the HTTP-network-or-cache fetch algorithm
351+
from the Fetch specification.
352+
1. Set |request|'s method to "POST".
353+
1. Set |request|'s URL to |destination|.
354+
1. If |signed challenge| is non-null, add header "Sec-Session-Response" with
355+
value |signed challenge| to |request|.
356+
1. Set |request|'s initiator to |originating request|'s initiator.
357+
1. Run HTTP-network-or-Dcache fetch on |request|.
358+
1. If the result is a 401 and has a "Sec-Session-Challenge" header, start
359+
this algorithm over with a new challenge value.
360+
1. If the result is a redirect, and the destination does not have the
361+
HTTPS scheme and is not localhost, cancel the request.
362+
1. Parse the response body according to {#format-session-instructions}.
363+
1. If the response contains the key "continue", with value "false":
364+
1. Delete the session with |destination|'s site and identifier the value of "session_identifier".
365+
1. Return from this algorithm.
366+
1. Otherwise, perform the following validations, returning if any fail:
367+
1. "session_identifier" must be non-empty.
368+
1. "refresh_url" must be non-empty.
369+
1. Let |origin| be "scope.origin", if present, or the origin of |destination| if not.
370+
1. |origin| must be a valid non-opaque origin.
371+
1. |origin| must be same-site with |destination|.
372+
1. Let |refresh url| be the result of resolving |destination| with the value of "refresh_url".
373+
1. |refresh url| must have scheme HTTPS or be localhost.
374+
1. |refresh url| must be same-site with |destination|.
375+
1. If the input |session id| is present, delete the session with site
376+
|destination|'s site and identifier |session id|.
377+
1. Create a new session with:
378+
1. [=session identifier=] the value of "session_identifier".
379+
1. [=refresh url=] set to |refresh url|.
380+
1. [=defer requests=] set to true.
381+
1. [=session scope=] a new scope with [=origin=] |origin|, [=include
382+
site=] the value of "scope.include_site", and [=scope
383+
specifications=] the value of "scope.scope_specification".
384+
1. [=session credentials=] the value of "credentials".
385+
1. [=session key=] a newly-generated key pair.
301386

302387
## Create session ## {#algo-create-session}
303388
To <dfn export id="create-session">Create a new session</dfn>, start with
@@ -327,16 +412,13 @@ parsing the registration structured header defined in
327412
Set |challenge| to |params|["challenge"].
328413
1. If |params|["authorization"] exists and is a string Set |authorization|
329414
to |params|["authorization"].
330-
1. Call [[#algo-session-request]] with |algorithm list|, |path|,
331-
|challenge| and |authorization| parameters.
415+
1. Create a |key pair| for |algorithm list|.
416+
1. Let |endpoint| be the result of resolving |path| relative to the
417+
|response|'s URL.
418+
1. Call [[#algo-session-request]] with |key pair|, |endpoint|, null
419+
|session id|, |challenge| and |authorization|.
332420
</div>
333421

334-
## Closing session ## {#algo-close-session}
335-
To <dfn export id="close-session">close a session</dfn>
336-
337-
## Fetch Integration ## {#algo-fetch-integration}
338-
To <dfn export id="fetch-integration">fetch Integration</dfn>
339-
340422
# DBSC Formats # {#format}
341423
## \``Sec-Session-Registration`\` HTTP header field ## {#header-sec-session-registration}
342424
The \`<dfn export http-header id="sec-session-registration-header">
@@ -627,11 +709,22 @@ In addition the following claims MUST be present if present in
627709
</div>
628710

629711
# Changes to other specifications # {#changes-to-other-specifications}
712+
630713
## Changes to the Fetch specification ## {#changes-to-fetch}
631-
--> Check if session should be refreshed before sending request
632-
- Alternatively add proof with Sec-Session-Response
633-
## Changes to the HTML specification ## {#changes-to-html}
634-
--> Clear Site Data: Clear the session if this is received
714+
715+
This specification requires an update the HTTP-network-or-cache fetch
716+
algorithm. Between steps 10.1 and 10.2 run
717+
[[#algo-identify-session-needing-refresh]]. If the result is non-null,
718+
run [[#algo-session-request]] with the returned |session|'s key pair,
719+
refresh URL, id, cached challenge, and an empty authorization.
720+
721+
## Changes to the Clear Site Data specification ## {#changes-to-clear-site-data}
722+
723+
This specification requires that the Clear Site Data specification
724+
sections 4.2.5 "Clear DOM-accessible storage for origin" clear all
725+
device bound sessions whose scope matches origin. It also requires an
726+
update to 4.2.4 to clear device bound sessions for the site matching the
727+
registered domain.
635728

636729
# IANA Considerations # {#iana-considerations}
637730

0 commit comments

Comments
 (0)