@@ -90,7 +90,7 @@ different situations. Some of the use cases of DBSC are:
90
90
transaction is legitimate.
91
91
</div>
92
92
93
- # Security Considerations # {#privacy }
93
+ # Security Considerations # {#security-considerations }
94
94
The goal of DBSC is to reduce session theft by offering an alternative to
95
95
long-lived cookie bearer tokens, that allows session authentication that is
96
96
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
103
103
that it is on the same device as the session was originally bound to if the
104
104
session was registered to a secure device.
105
105
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
+
106
109
## Non-goals ## {#non-goals}
107
110
DBSC will not prevent temporary access to the browser session while the attacker
108
111
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
154
157
whether or not a user is authenticated by measuring how long the request takes
155
158
as the refresh is quite slow, partially due to the latency of TPM operations.
156
159
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.
168
163
169
164
# Alternatives considered # {#alternatives}
170
165
## WebAuthn and silent mediation ## {#alternatives-webauthn}
@@ -206,19 +201,25 @@ A <dfn>device bound session</dfn> is a [=struct=] with the following
206
201
:: a [=string=] that is to be used as the next challenge for this session
207
202
: [=session scope=]
208
203
:: 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}.
211
211
</dl>
212
212
213
213
## Session scope ## {#framework-scope}
214
214
The <dfn>session scope</dfn> is a [=struct=] with the following
215
215
[=struct/items=] :
216
216
<dl dfn-for="session scope">
217
+ : <dfn>origin</dfn>
218
+ :: The [=origin=] this session was registered for.
217
219
: <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
222
223
</dl>
223
224
224
225
## Scope specification ## {#framework-scope-specification}
@@ -228,8 +229,10 @@ The <dfn>scope specification</dfn> is a [=struct=] with the following
228
229
: <dfn>type</dfn>
229
230
:: a [=string=] to be either "include" or "exclude", defining if the item
230
231
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.
231
234
: <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
233
236
</dl>
234
237
235
238
## Session credential ## {#framework-session-credential}
@@ -261,16 +264,59 @@ The <dfn>session credential</dfn> is a [=struct=] with the following
261
264
1. Return |domain sessions|[|session identifier|]
262
265
</div>
263
266
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">
266
313
This algorithm describes how to
267
314
<dfn export dfn-for="algorithms">process a challenge</dfn> received in an HTTP
268
315
header.
269
316
270
317
Given a [=response=] (|response|) and a [=sessions by registrable domain=] , this
271
318
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=] .
274
320
275
321
1. Let |header name| be "<code> Sec-Session-Challenge</code> ".
276
322
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
284
330
1. Let |session id| be null.
285
331
1. If params["id"] exists and is a <a>sf-string</a> , Set |session id| to
286
332
params["id"] .
287
- 1. If [=response/status=] is 401, resend this request as is with updated
288
- |challenge| in [=DBSC proof=] and [=iteration/continue=] .
289
333
1. If |session id| is null, [=iteration/continue=] .
290
334
1. Identify session as described in [=identify a session=] given
291
335
|response| and |session id| and store as |session object|.
@@ -294,10 +338,51 @@ The <dfn>session credential</dfn> is a [=struct=] with the following
294
338
[=DBSC proof=] is to be sent from this [=device bound session=] .
295
339
</div>
296
340
297
- ## Session refresh ## {#algo-session-refresh}
298
- To <dfn export id="refresh-session">Refreshing an existing session</dfn>
299
-
300
341
## 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.
301
386
302
387
## Create session ## {#algo-create-session}
303
388
To <dfn export id="create-session">Create a new session</dfn> , start with
@@ -327,16 +412,13 @@ parsing the registration structured header defined in
327
412
Set |challenge| to |params|["challenge"] .
328
413
1. If |params|["authorization"] exists and is a string Set |authorization|
329
414
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|.
332
420
</div>
333
421
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
-
340
422
# DBSC Formats # {#format}
341
423
## \``Sec-Session-Registration`\` HTTP header field ## {#header-sec-session-registration}
342
424
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
627
709
</div>
628
710
629
711
# Changes to other specifications # {#changes-to-other-specifications}
712
+
630
713
## 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.
635
728
636
729
# IANA Considerations # {#iana-considerations}
637
730
0 commit comments