From nobody Fri Dec  4 01:22:41 2020
Return-Path: <philippe@pragmaticwebsecurity.com>
X-Original-To: oauth@ietfa.amsl.com
Delivered-To: oauth@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1])
 by ietfa.amsl.com (Postfix) with ESMTP id 7D7093A0B6D
 for <oauth@ietfa.amsl.com>; Fri,  4 Dec 2020 01:22:40 -0800 (PST)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -2.119
X-Spam-Level: 
X-Spam-Status: No, score=-2.119 tagged_above=-999 required=5
 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1,
 DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, HTML_MESSAGE=0.001,
 RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_PASS=-0.001,
 URIBL_BLOCKED=0.001] autolearn=ham autolearn_force=no
Authentication-Results: ietfa.amsl.com (amavisd-new); dkim=pass (2048-bit key)
 header.d=pragmaticwebsecurity.com header.b=bsc6JBye;
 dkim=pass (2048-bit key) header.d=messagingengine.com
 header.b=Ap8YyULP
Received: from mail.ietf.org ([4.31.198.44])
 by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024)
 with ESMTP id ylWs-BbTsCC4 for <oauth@ietfa.amsl.com>;
 Fri,  4 Dec 2020 01:22:36 -0800 (PST)
Received: from wout3-smtp.messagingengine.com (wout3-smtp.messagingengine.com
 [64.147.123.19])
 (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits))
 (No client certificate requested)
 by ietfa.amsl.com (Postfix) with ESMTPS id 3385D3A09B5
 for <oauth@ietf.org>; Fri,  4 Dec 2020 01:22:36 -0800 (PST)
Received: from compute3.internal (compute3.nyi.internal [10.202.2.43])
 by mailout.west.internal (Postfix) with ESMTP id 093D8B27;
 Fri,  4 Dec 2020 04:22:34 -0500 (EST)
Received: from mailfrontend1 ([10.202.2.162])
 by compute3.internal (MEProxy); Fri, 04 Dec 2020 04:22:35 -0500
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=
 pragmaticwebsecurity.com; h=from:message-id:content-type
 :mime-version:subject:date:in-reply-to:cc:to:references; s=fm2;
 bh=Xta/mWOoAvbv2wTS/p7ZSUP6GhspU+YZPczXkv2g0Ks=; b=bsc6JByeTS+1
 SCW7U0ENawl6yZk5X9hRiZIse1XivhBs//9AXBNGK+QsmKFwrjM03MGtnR0Jb3by
 17gauKasxSssQpDXH42VAgCSJ3zfgdujskvkK11T56odSxR8B9BnxUSG7Vx/4AR4
 5vd9xxsiJrgwBBRZzeg+5SjjYfuyPvM9PM4+QBTcUNW9+/42Qo52J9PQE3aPiYLC
 PC4ivYGdfM3Pt0pMJwfzcoE1Gd7QdLnDW++UdAP14Ac8YUmNkISeIylduQtokvMg
 Gm+kcfcPYuiyohYpeG6L3BfwImEtFHY7HRTfuuHm+s8/DhB+Nwpt7reIuZnEpbHg
 TBRqYmJ3Iw==
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=
 messagingengine.com; h=cc:content-type:date:from:in-reply-to
 :message-id:mime-version:references:subject:to:x-me-proxy
 :x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm1; bh=Xta/mW
 OoAvbv2wTS/p7ZSUP6GhspU+YZPczXkv2g0Ks=; b=Ap8YyULP4zgPm9pRtleBJo
 +btDnYtAOAMx6LlYb/hbtji8I/JjCPJJReKIcs5CYn3ybxfHgE5YugqTDAxwgPzf
 Uh/Quz8SOLXKhZQiBWcM5sawD93IXypRJG/9+P4roQ7OQXMQCEcsp4Xf2KjNFxdm
 pi4u/L5c+M12lE7dkMMDxhaB3zcJBxi1f/53Z5s+heGkzxWcdFPQehJ0Kkbs9eU1
 uEGYhRLbPP07q4/7iXy1OdHqibrxruVyuiaLqfGM8ccRCU8tSA9daj4HxmNZHgTC
 f4R23MrKJrFMHvnKjeyYadhq8Q2QRTTR/FVsZpUrxnSP0GUnB/tkPIHEEOffRR+Q
 ==
X-ME-Sender: <xms:2v_JX81KSbtOKEE_WYLUtFy450MYYxTtpkKWrWg5eq91Jifxs7k6kA>
 <xme:2v_JX3EjW5o_jwMw4fDu5K9jvWZUwHByawGLr5aS7uVIIOHleA8R69TGseunMMeot
 EqlFACtWytZl1f_QQ>
X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedujedrudeikedgtddvucetufdoteggodetrfdotf
 fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen
 uceurghilhhouhhtmecufedttdenucenucfjughrpefhkfgtggfuffgjvfhfofesrgdtmh
 erhhdtjeenucfhrhhomheprfhhihhlihhpphgvucffvgcutfihtghkuceophhhihhlihhp
 phgvsehprhgrghhmrghtihgtfigvsghsvggtuhhrihhthidrtghomheqnecuggftrfgrth
 htvghrnhepieevfefhgedvudetieeffedvleeijeehvefhteduledthefffffgtefhkeet
 ueeinecuffhomhgrihhnpegurghnihgvlhhfvghtthdruggvpdhprhgrghhmrghtihgtfi
 gvsghsvggtuhhrihhthidrtghomhdpohhpvghnihgurdhnvghtpdgsihhtsghutghkvght
 rdhorhhgpdhivghtfhdrohhrghdpghhoohhglhgvrdgtohhmnecukfhppeekuddrudeige
 druddvledrleehnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhf
 rhhomhepphhhihhlihhpphgvsehprhgrghhmrghtihgtfigvsghsvggtuhhrihhthidrtg
 homh
X-ME-Proxy: <xmx:2v_JX071MTwYxFjo5Y4KGgZcqLOLjbLBGHmKClZphNMNxMrV2WG_FA>
 <xmx:2v_JX13ydJD5egTLyckaiFvBsG1A_gSV8uleDxQlRU16QUnK5nRFKg>
 <xmx:2v_JX_GDiuPJF-JgO6VoL7220MBc5pdZ_b9CfOqP9n4QN-4d6-m6UQ>
 <xmx:2v_JX1MMYyfCKmqxLxIZoNdXULeo83ncRi4Pnb5Xm7kJKW12r190Kg>
Received: from [192.168.1.47] (d51a4815f.access.telenet.be [81.164.129.95])
 by mail.messagingengine.com (Postfix) with ESMTPA id 7BEFC24005A;
 Fri,  4 Dec 2020 04:22:33 -0500 (EST)
From: Philippe De Ryck <philippe@pragmaticwebsecurity.com>
Message-Id: <CE661132-5D86-4652-B115-E6089E39BC68@pragmaticwebsecurity.com>
Content-Type: multipart/alternative;
 boundary="Apple-Mail=_A12DDCD6-4B0C-4FB5-B013-D015F7C69B2D"
Mime-Version: 1.0 (Mac OS X Mail 13.4 \(3608.120.23.2.4\))
Date: Fri, 4 Dec 2020 10:22:31 +0100
In-Reply-To: <353E4494-2F80-44BC-9267-6FB8B37AA0FE@lodderstedt.net>
Cc: Filip Skokan <panva.ip@gmail.com>,
 Brian Campbell <bcampbell=40pingidentity.com@dmarc.ietf.org>,
 oauth <oauth@ietf.org>
To: Torsten Lodderstedt <torsten=40lodderstedt.net@dmarc.ietf.org>
References: <CA+k3eCSyMWyLYorWH7KY+XR1syAQUi4tQXdUuevKz4Y6xNMheA@mail.gmail.com>
 <CALAqi_8zWGAG8p5OnrTB=4q_jL00dJi5oYQrWXmzvqKGhfL28w@mail.gmail.com>
 <30D5F7BA-EA54-4E40-A2FC-222AA0C9AF8D@lodderstedt.net>
 <CALAqi_9AH5O6d2W+0UDz83=Csm9BbcU8j6qiRxz5rzLfzm6AQA@mail.gmail.com>
 <353E4494-2F80-44BC-9267-6FB8B37AA0FE@lodderstedt.net>
X-Mailer: Apple Mail (2.3608.120.23.2.4)
Archived-At: <https://mailarchive.ietf.org/arch/msg/oauth/kH3tyrjz18d1yL3PUxjDmn8Ic20>
Subject: Re: [OAUTH-WG] DPoP followup I: freshness and coverage of signature
X-BeenThere: oauth@ietf.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: OAUTH WG <oauth.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/oauth>,
 <mailto:oauth-request@ietf.org?subject=unsubscribe>
List-Archive: <https://mailarchive.ietf.org/arch/browse/oauth/>
List-Post: <mailto:oauth@ietf.org>
List-Help: <mailto:oauth-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/oauth>,
 <mailto:oauth-request@ietf.org?subject=subscribe>
X-List-Received-Date: Fri, 04 Dec 2020 09:22:41 -0000


--Apple-Mail=_A12DDCD6-4B0C-4FB5-B013-D015F7C69B2D
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain;
	charset=utf-8

Hi all,

This is a very useful discussion, and there are some merits to using =
DPoP in this way. However, the attacker's capabilities are stronger than =
often assumed, so it may not matter in the end. I've been wanting to =
write this out for a while now, so I've added a couple of scenarios =
below. Note that I just came up with the scenario names on the fly, so =
these may not be the best ones for future use ...

(This got a lot longer than I expected, so here's a TOC)
- Attack assumption
- Scenario 1: offline XSS against existing tokens
- Scenario 2: passive online XSS against existing tokens
- Scenario 3: active online XSS against existing tokens
- Scenario 4 (!): obtaining fresh tokens
- Mitigation: DPoP in a Web Worker
- Conclusion (TL;DR)

I hope this all makes sense!

Philippe




Assumption

The attacker has the ability to execute JS code in the application's =
context (e.g., through XSS, a malicious ad, ...). For simplicity, I'll =
just refer to the attack as "XSS".



Scenario 1: offline XSS against existing tokens

In this scenario, the malicious code executes and immediately performs a =
malicious action. The attacker is not necessarily present or actively =
participating in the attack (i.e., abuse of stolen tokens is done at a =
later time).=20

A common example would be stealing tokens from localStorage and sending =
them to an attacker-controlled server for later abuse. Existing =
mitigations include short AT lifetimes and RT rotation.

The attacker could determine that DPoP is being used, and also extract =
precomputed proofs for any of these tokens. The use of DPoP makes token =
abuse a bit harder (large window =3D lots of proofs), but does not =
really strengthen the defense beyond existing mitigations (Short AT =
lifetimes and RT rotation).=20



Scenario 2: passive online XSS against existing tokens

In this scenario, the malicious code executes and sets up a long-term =
attack. The attacker (i.e., a malicious application running on a server) =
is passive until certain criteria are met.=20

An attack could be to manipulate the JS execution context, so that the =
attacker can detect new tokens being obtained by the client. (e.g., by =
overriding a listener or changing core function prototypes). Each time =
new tokens are issued (AT + RT), the attacker sends them to the =
malicious server. The moment the attacker detects that the user closes =
the application, the malicious server continues the RT rotation chain. =
Since the application is no longer active, the AS will not detect this. =
The attacker now has access for as long as the RT chain can be kept =
alive.

When DPoP is used, the attacker will need proofs to present to the AS =
when running a refresh token flow. If the proofs are independent of the =
RT being used, these can be precomputed. When the RT is part of the =
proof, as per Filip's suggestion, the attacker can only run a RT flow =
once (with the stolen RT + proof). This attack scenario is fairly well =
mitigated when DPoP proofs include the RT (hash).



Scenario 3: active online XSS against existing tokens

In this scenario, the malicious code executes and sets up a long-term =
attack. The attacker is actively controlling the behavior of the =
malicious code.=20

The attack vectors are the same as scenario 2. Once in control, the =
attacker can use the same mechanism as the application does to send =
requests to any endpoint. There is no need to obtain an RT (which may =
not even be possible), since the attacker can just abuse the AT =
directly.

When DPoP is used, little changes here. The attacker can use the =
application's DPoP mechanism to obtain legitimate proofs. DPoP does =
nothing to mitigate this type of attack (as already stated in Daniel's =
threat model: https://danielfett.de/2020/05/04/dpop-attacker-model/).



Scenario 4: obtaining fresh tokens

In this scenario, the malicious code executes and immediately launches =
the attack. In this attack, the malicious code loads a hidden iframe in =
the application's DOM. In that iframe, the attacker starts a silent flow =
with AS to obtain an authorization code (AC). If the user has an active =
session, this will succeed (existing cookie + all origins match). The =
attacker extracts this AC and exchanges it for tokens with the AS.=20

At this point, the attacker has a fresh set of tokens that grant access =
to resources in the name of the user. Short AT lifetimes and RT rotation =
are useless, since the attacker is in full control of the tokens.

Using DPoP in this scenario does not help at all. The attacker can use =
their own private key to generate the necesary DPoP proofs, starting =
with the code exchange.

One solution is to turn off silent flows for SPAs, since they have =
become quite unreliable with third-party cookie blocking restrictions.



Mitigation: DPoP in a Web Worker

Isolating sensitive features from malicious JS is virtually impossible =
when the application's legitimate JS code needs access to them. One =
solution that can work is the use of a Web Worker. Concretely, the DPoP =
private key is kept in a worker, isolated from the main application. All =
requests that require a DPoP proof are tunneled through the worker, =
which adds the required proof.=20

In this setup, the worker can also handle RTs, since they will require a =
proof. This strategy is already implemented by some libraries (e.g., =
Auth0's JS library). When ATs are also sender-constrained with DPoP, =
they can also be kept in the worker instead of in the application.

This implementation strategy has the following impact on the scenarios =
discussed above.

- Scenario 1 (offline XSS): mitigated, since there is nothing to steal =
from the application
- Scenario 2 (passive online XSS): mitigated, since there are no tokens =
to steal
- Scenario 3 (active online XSS): impacted, but not mitigated. The =
attacker can no longer send requests directly, and is forced to go =
through the worker. The worker can enforce restrictions on requests it =
sends (e.g., rate limiting, only allowing certain types of requests or =
certain endpoints, ...)
- Scenario 4 (obtaining fresh tokens): no effect --> this is a big =
problem!



Conclusion

Even with current best practices and DPoP in place, an attacker can =
still send requests through the application, or obtain an entirely fresh =
set of tokens with a hidden silent flow. The former scenario can be =
somewhat reduced, but the latter cannot be mitigated.=20

In a nutshell: XSS =3D Game over


=E2=80=94
Pragmatic Web Security
Security for developers
https://pragmaticwebsecurity.com/


> On 3 Dec 2020, at 13:55, Torsten Lodderstedt =
<torsten=3D40lodderstedt.net@dmarc.ietf.org> wrote:
>=20
> I understand. Thanks!=20
>=20
> I think RT rotation + RT hash in the proof would also stop the attack. =
=20
>=20
>> Am 03.12.2020 um 13:19 schrieb Filip Skokan <panva.ip@gmail.com>:
>>=20
>> I'm failing to understand why binding the proof to the access token =
ensures freshness of the proof.
>>=20
>> Because when access tokens issued to public browser based clients =
have a short duration you need continued access to the private key to =
issue new proofs. When I exfiltrate the RT and pre-generate tons of =
proofs while the user is active on the page through XSS I can then use =
the RT and my prepared proofs to talk to the AS to keep on refreshing =
the AT and use it against the RS. When the value of the token is part of =
the proof, i cannot pre-generate them for future issued access tokens. =
Short `iat` based windows don't help here.
>>=20
>> S pozdravem,
>> Filip Skokan
>>=20
>>=20
>> On Thu, 3 Dec 2020 at 12:59, Torsten Lodderstedt =
<torsten@lodderstedt.net> wrote:
>> Hi,=20
>>=20
>> I'm failing to understand why binding the proof to the access token =
ensures freshness of the proof. I would rather think if the client is =
forced to create proofs with a reasonable short lifetime, chances for =
replay could be reduced.=20
>>=20
>> Beside that as far as I remember the primary replay counter measure =
is the inclusion of the endpoint URL and HTTP method in the proof, since =
it reduces the attack surface to a particular URL. So in the context of =
freshness, we are talking about using the same proof with the same URL =
again.=20
>>=20
>> best regards,
>> Torsten.=20
>>=20
>>> Am 03.12.2020 um 10:20 schrieb Filip Skokan <panva.ip@gmail.com>:
>>>=20
>>> Hi Brian, everyone,
>>>=20
>>> While the attack vector allows direct use, there is the option where =
a smarter attacker will not abuse the gained artifacts straight away. =
Think public client browser scenario with the non-extractable private =
key stored in IndexedDB (the only place to persist them really), they =
wouldn't use the tokens but instead, exfiltrate them, together with a =
bunch of pre-generated DPoP proofs. They'll get the refresh token and a =
bunch of DPoP proofs for both the RS and AS. With those they'll be able =
to get a fresh AT and use it with pre-generated Proofs after the =
end-user leaves the site. No available protection (e.g. RT already =
rotated) will be able to kick in until the end-user opens the page =
again.
>>>=20
>>> OTOH with a hash of the AT in the Proof only direct use remains.
>>>=20
>>> If what I describe above is something we don't want to deal with =
because of direct use already allowing access to protected resources, =
it's sufficiently okay as is (option #1). However, if this scenario, one =
allowing prolonged access to protected resources, is not acceptable, =
it's option #2.
>>>=20
>>> Ad #2a vs #2b vs #2c. My pre-emptive answer is #2a, simply because =
we already have the tools needed to generate and validate these hashes. =
But further thinking about it, it would feel awkward if this JWS =
algorithm driven at_hash digest selection wouldn't get stretched to the =
confirmations, when this are placed in a JWT access token, cool - we can =
do that, but when these are put in a basic token introspection response =
it's unfortunately not an option. So, #2b (just use sha-256 just like =
the confirmations do).
>>>=20
>>> Best,
>>> Filip
>>>=20
>>>=20
>>> On Wed, 2 Dec 2020 at 21:50, Brian Campbell =
<bcampbell=3D40pingidentity.com@dmarc.ietf.org> wrote:
>>> There were a few items discussed somewhat during the recent interim =
that I committed to bringing back to the list. The slide below (also =
available as slide #17 from the interim presentation) is the first one =
of them, which is difficult to summarize but kinda boils down to how =
much assurance there is that the DPoP proof was 'freshly' created and =
that can dovetail into the question of whether the token is covered by =
the signature of the proof.=20
>>> There are many directions a "resolution" here could go but my sense =
of the room during the meeting was that the contending options were:
>>>      =E2=80=A2  It's sufficiently okay as it is
>>>      =E2=80=A2  Include a hash of the access token in the DPoP proof =
(when an access token is present)
>>>=20
>>> Going with #2 would mean the draft would also have to define how the =
hashing is done and deal with or at least speak to algorithm agility. =
Options (that I can think of) include:
>>>      =E2=80=A2 2a) Use the at_hash claim defined in OIDC core =
https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken. Using =
something that already exists is appealing. But its hash alg selection =
routine can be a bit of a pain. And the algorithm agility based on the =
signature that it's supposed to provide hasn't worked out as well as =
hoped in practice for "new" JWS signatures =
https://bitbucket.org/openid/connect/issues/1125/_hash-algorithm-for-eddsa=
-id-tokens
>>>      =E2=80=A2 2b) Define a new claim ("ah", "ath", "atd", "ad" or =
something like that maybe) and just use SHA-256. Explain why it's good =
enough for now and the foreseeable future. Also include some text about =
introducing a new claim in the future if/when SHA-256 proves to be =
insufficient. Note that this is effectively the same as how the =
confirmation claim value is currently defined in this document and in =
RFC8705.
>>>      =E2=80=A2 2c) Define a new claim with its own hash algorithm =
agility scheme (likely similar to how the Digest header value or =
Subresource Integrity string is done).
>>>=20
>>> I'm requesting that interested WG participants indicate their =
preference for #1 or #2. And among a, b, and c, if the latter.=20
>>>=20
>>> I also acknowledge that an ECDH approach could/would ameliorate the =
issues in a fundamentally different way. But that would be a distinct =
protocol. If there's interest in pursuing the ECDH idea, I'm certainly =
open to it and even willing to work on it. But as a separate effort and =
not at the expense of derailing DPoP in its general current form.=20
>>> <Slide17.jpeg>
>>>=20
>>>=20
>>> CONFIDENTIALITY NOTICE: This email may contain confidential and =
privileged material for the sole use of the intended recipient(s). Any =
review, use, distribution or disclosure by others is strictly =
prohibited.  If you have received this communication in error, please =
notify the sender immediately by e-mail and delete the message and any =
file attachments from your computer. Thank =
you._______________________________________________
>>> OAuth mailing list
>>> OAuth@ietf.org
>>> https://www.ietf.org/mailman/listinfo/oauth
>>> _______________________________________________
>>> OAuth mailing list
>>> OAuth@ietf.org
>>> =
https://www.google.com/url?q=3Dhttps://www.ietf.org/mailman/listinfo/oauth=
&source=3Dgmail-imap&ust=3D1607592086000000&usg=3DAOvVaw3hGaihxAdyXVvzFnVT=
pc6N
>>=20
>=20
> _______________________________________________
> OAuth mailing list
> OAuth@ietf.org
> https://www.ietf.org/mailman/listinfo/oauth


--Apple-Mail=_A12DDCD6-4B0C-4FB5-B013-D015F7C69B2D
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html;
	charset=utf-8

<html><head><meta http-equiv=3D"Content-Type" content=3D"text/html; =
charset=3Dutf-8"></head><body style=3D"word-wrap: break-word; =
-webkit-nbsp-mode: space; line-break: after-white-space;" class=3D""><div =
class=3D"">Hi all,</div><div class=3D""><br class=3D""></div><div =
class=3D"">This is a very useful discussion, and there are some merits =
to using DPoP in this way. However, the attacker's capabilities are =
stronger than often assumed, so it may not matter in the end. I've been =
wanting to write this out for a while now, so I've added a couple of =
scenarios below. Note that I just came up with the scenario names on the =
fly, so these may not be the best ones for future use ...</div><div =
class=3D""><br class=3D""></div><div class=3D"">(This got a lot longer =
than I expected, so here's a TOC)</div><div class=3D"">- Attack =
assumption</div><div class=3D"">- Scenario 1: offline XSS against =
existing tokens</div><div class=3D"">- Scenario 2: passive online XSS =
against existing tokens</div><div class=3D"">- Scenario 3: active online =
XSS against existing tokens</div><div class=3D"">- Scenario 4 (!): =
obtaining fresh tokens</div><div class=3D"">- Mitigation: DPoP in a Web =
Worker</div><div class=3D"">- Conclusion (TL;DR)</div><div class=3D""><br =
class=3D""></div><div class=3D"">I hope this all makes sense!</div><div =
class=3D""><br class=3D""></div><div class=3D"">Philippe</div><div =
class=3D""><br class=3D""></div><div class=3D""><br class=3D""></div><div =
class=3D""><br class=3D""></div><div class=3D""><br class=3D""></div><div =
class=3D""><b class=3D""><u class=3D"">Assumption</u></b></div><div =
class=3D""><br class=3D""></div><div class=3D"">The attacker has the =
ability to execute JS code in the application's context (e.g., through =
XSS, a malicious ad, ...). For simplicity, I'll just refer to the attack =
as "XSS".</div><div class=3D""><br class=3D""></div><div class=3D""><br =
class=3D""></div><div class=3D""><br class=3D""></div><div class=3D""><b =
class=3D""><u class=3D"">Scenario 1: offline XSS against existing =
tokens</u></b></div><div class=3D""><br class=3D""></div><div =
class=3D"">In this scenario, the malicious code executes and immediately =
performs a malicious action. The attacker is not necessarily present or =
actively participating in the attack (i.e., abuse of stolen tokens is =
done at a later time).&nbsp;</div><div class=3D""><br =
class=3D""></div><div class=3D"">A common example would be stealing =
tokens from localStorage and sending them to an attacker-controlled =
server for later abuse. Existing mitigations include short AT lifetimes =
and RT rotation.</div><div class=3D""><br class=3D""></div><div =
class=3D"">The attacker could determine that DPoP is being used, and =
also extract precomputed proofs for any of these tokens. The use of DPoP =
makes token abuse a bit harder (large window =3D lots of proofs), but =
does not really strengthen the defense beyond existing mitigations =
(Short AT lifetimes and RT rotation).&nbsp;</div><div class=3D""><br =
class=3D""></div><div class=3D""><br class=3D""></div><div class=3D""><br =
class=3D""></div><div class=3D""><b class=3D""><u class=3D"">Scenario 2: =
passive online XSS against existing tokens</u></b></div><div =
class=3D""><br class=3D""></div><div class=3D"">In this scenario, the =
malicious code executes and sets up a long-term attack. The attacker =
(i.e., a malicious application running on a server) is passive until =
certain criteria are met.&nbsp;</div><div class=3D""><br =
class=3D""></div><div class=3D"">An attack could be to manipulate the JS =
execution context, so that the attacker can detect new tokens being =
obtained by the client. (e.g., by overriding a listener or changing core =
function prototypes). Each time new tokens are issued (AT + RT), the =
attacker sends them to the malicious server. The moment the attacker =
detects that the user closes the application, the malicious server =
continues the RT rotation chain. Since the application is no longer =
active, the AS will not detect this. The attacker now has access for as =
long as the RT chain can be kept alive.</div><div class=3D""><br =
class=3D""></div><div class=3D"">When DPoP is used, the attacker will =
need proofs to present to the AS when running a refresh token flow. If =
the proofs are independent of the RT being used, these can be =
precomputed. When the RT is part of the proof, as per Filip's =
suggestion, the attacker can only run a RT flow once (with the stolen RT =
+ proof). This attack scenario is fairly well mitigated when DPoP proofs =
include the RT (hash).</div><div class=3D""><br class=3D""></div><div =
class=3D""><br class=3D""></div><div class=3D""><br class=3D""></div><div =
class=3D""><b class=3D""><u class=3D"">Scenario 3: active online XSS =
against existing tokens</u></b></div><div class=3D""><br =
class=3D""></div><div class=3D"">In this scenario, the malicious code =
executes and sets up a long-term attack. The attacker is actively =
controlling the behavior of the malicious code.&nbsp;</div><div =
class=3D""><br class=3D""></div><div class=3D"">The attack vectors are =
the same as scenario 2. Once in control, the attacker can use the same =
mechanism as the application does to send requests to any endpoint. =
There is no need to obtain an RT (which may not even be possible), since =
the attacker can just abuse the AT directly.</div><div class=3D""><br =
class=3D""></div><div class=3D"">When DPoP is used, little changes here. =
The attacker can use the application's DPoP mechanism to obtain =
legitimate proofs. DPoP does nothing to mitigate this type of attack (as =
already stated in Daniel's threat model: <a =
href=3D"https://danielfett.de/2020/05/04/dpop-attacker-model/" =
class=3D"">https://danielfett.de/2020/05/04/dpop-attacker-model/</a>).</di=
v><div class=3D""><br class=3D""></div><div class=3D""><br =
class=3D""></div><div class=3D""><br class=3D""></div><div class=3D""><b =
class=3D""><u class=3D"">Scenario 4: obtaining fresh =
tokens</u></b></div><div class=3D""><br class=3D""></div><div =
class=3D"">In this scenario, the malicious code executes and immediately =
launches the attack. In this attack, the malicious code loads a hidden =
iframe in the application's DOM. In that iframe, the attacker starts a =
silent flow with AS to obtain an authorization code (AC). If the user =
has an active session, this will succeed (existing cookie + all origins =
match). The attacker extracts this AC and exchanges it for tokens with =
the AS.&nbsp;</div><div class=3D""><br class=3D""></div><div class=3D"">At=
 this point, the attacker has a fresh set of tokens that grant access to =
resources in the name of the user. Short AT lifetimes and RT rotation =
are useless, since the attacker is in full control of the =
tokens.</div><div class=3D""><br class=3D""></div><div class=3D"">Using =
DPoP in this scenario does not help at all. The attacker can use their =
own private key to generate the necesary DPoP proofs, starting with the =
code exchange.</div><div class=3D""><br class=3D""></div><div =
class=3D"">One solution is to turn off silent flows for SPAs, since they =
have become quite unreliable with third-party cookie blocking =
restrictions.</div><div class=3D""><br class=3D""></div><div =
class=3D""><br class=3D""></div><div class=3D""><br class=3D""></div><div =
class=3D""><b class=3D""><u class=3D"">Mitigation: DPoP in a Web =
Worker</u></b></div><div class=3D""><br class=3D""></div><div =
class=3D"">Isolating sensitive features from malicious JS is virtually =
impossible when the application's legitimate JS code needs access to =
them. One solution that can work is the use of a Web Worker. Concretely, =
the DPoP private key is kept in a worker, isolated from the main =
application. All requests that require a DPoP proof are tunneled through =
the worker, which adds the required proof.&nbsp;</div><div class=3D""><br =
class=3D""></div><div class=3D"">In this setup, the worker can also =
handle RTs, since they will require a proof. This strategy is already =
implemented by some libraries (e.g., Auth0's JS library). When ATs are =
also sender-constrained with DPoP, they can also be kept in the worker =
instead of in the application.</div><div class=3D""><br =
class=3D""></div><div class=3D"">This implementation strategy has the =
following impact on the scenarios discussed above.</div><div =
class=3D""><br class=3D""></div><div class=3D""><b class=3D""><i =
class=3D"">- Scenario 1 (offline XSS):</i></b> mitigated, since there is =
nothing to steal from the application</div><div class=3D""><b =
class=3D""><i class=3D"">- Scenario 2 (passive online XSS): =
</i></b>mitigated, since there are no tokens to steal</div><div =
class=3D""><b class=3D""><i class=3D"">- Scenario 3 (active online =
XSS):</i></b> impacted, but not mitigated. The attacker can no longer =
send requests directly, and is forced to go through the worker. The =
worker can enforce restrictions on requests it sends (e.g., rate =
limiting, only allowing certain types of requests or certain endpoints, =
...)</div><div class=3D""><b class=3D""><i class=3D"">- Scenario 4 =
(obtaining fresh tokens):</i></b> no effect --&gt; this is a big =
problem!</div><div class=3D""><br class=3D""></div><div class=3D""><br =
class=3D""></div><div class=3D""><br class=3D""></div><div class=3D""><div=
 class=3D""><b class=3D""><u class=3D"">Conclusion</u></b></div><div =
class=3D""><br class=3D""></div><div class=3D"">Even with current best =
practices and DPoP in place, an attacker can still send requests through =
the application, or obtain an entirely fresh set of tokens with a hidden =
silent flow. The former scenario can be somewhat reduced, but the latter =
cannot be mitigated.&nbsp;</div><div class=3D""><br class=3D""></div><div =
class=3D"">In a nutshell: XSS =3D Game over</div></div><div class=3D""><br=
 class=3D""></div><div class=3D""><br class=3D""></div><div class=3D"">
<div dir=3D"auto" style=3D"word-wrap: break-word; -webkit-nbsp-mode: =
space; line-break: after-white-space;" class=3D""><div =
style=3D"caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-family: =
Helvetica; font-size: 12px; font-style: normal; font-variant-caps: =
normal; font-weight: normal; letter-spacing: normal; text-align: start; =
text-indent: 0px; text-transform: none; white-space: normal; =
word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration: =
none;">=E2=80=94<br class=3D""><b class=3D"">Pragmatic Web =
Security</b><br class=3D""><i class=3D"">Security for developers</i><br =
class=3D""><a href=3D"https://pragmaticwebsecurity.com/" =
class=3D"">https://pragmaticwebsecurity.com/</a><br class=3D""><br =
class=3D""></div></div>
</div>
<div><br class=3D""><blockquote type=3D"cite" class=3D""><div =
class=3D"">On 3 Dec 2020, at 13:55, Torsten Lodderstedt &lt;<a =
href=3D"mailto:torsten=3D40lodderstedt.net@dmarc.ietf.org" =
class=3D"">torsten=3D40lodderstedt.net@dmarc.ietf.org</a>&gt; =
wrote:</div><br class=3D"Apple-interchange-newline"><div class=3D""><div =
class=3D"">I understand. Thanks! <br class=3D""><br class=3D"">I think =
RT rotation + RT hash in the proof would also stop the attack. &nbsp;<br =
class=3D""><br class=3D""><blockquote type=3D"cite" class=3D"">Am =
03.12.2020 um 13:19 schrieb Filip Skokan &lt;<a =
href=3D"mailto:panva.ip@gmail.com" =
class=3D"">panva.ip@gmail.com</a>&gt;:<br class=3D""><br class=3D"">I'm =
failing to understand why binding the proof to the access token ensures =
freshness of the proof.<br class=3D""><br class=3D"">Because when access =
tokens issued to public browser based clients have a short duration you =
need continued access to the private key to issue new proofs. When I =
exfiltrate the RT and pre-generate tons of proofs while the user is =
active on the page through XSS I can then use the RT and my prepared =
proofs to talk to the AS to keep on refreshing the AT and use it against =
the RS. When the value of the token is part of the proof, i cannot =
pre-generate them for future issued access tokens. Short `iat` based =
windows don't help here.<br class=3D""><br class=3D"">S pozdravem,<br =
class=3D"">Filip Skokan<br class=3D""><br class=3D""><br class=3D"">On =
Thu, 3 Dec 2020 at 12:59, Torsten Lodderstedt &lt;<a =
href=3D"mailto:torsten@lodderstedt.net" =
class=3D"">torsten@lodderstedt.net</a>&gt; wrote:<br class=3D"">Hi, <br =
class=3D""><br class=3D"">I'm failing to understand why binding the =
proof to the access token ensures freshness of the proof. I would rather =
think if the client is forced to create proofs with a reasonable short =
lifetime, chances for replay could be reduced. <br class=3D""><br =
class=3D"">Beside that as far as I remember the primary replay counter =
measure is the inclusion of the endpoint URL and HTTP method in the =
proof, since it reduces the attack surface to a particular URL. So in =
the context of freshness, we are talking about using the same proof with =
the same URL again. <br class=3D""><br class=3D"">best regards,<br =
class=3D"">Torsten. <br class=3D""><br class=3D""><blockquote =
type=3D"cite" class=3D"">Am 03.12.2020 um 10:20 schrieb Filip Skokan =
&lt;<a href=3D"mailto:panva.ip@gmail.com" =
class=3D"">panva.ip@gmail.com</a>&gt;:<br class=3D""><br class=3D"">Hi =
Brian, everyone,<br class=3D""><br class=3D"">While the attack vector =
allows direct use, there is the option where a smarter attacker will not =
abuse the gained artifacts straight away. Think public client browser =
scenario with the non-extractable private key stored in IndexedDB (the =
only place to persist them really), they wouldn't use the tokens but =
instead, exfiltrate them, together with a bunch of pre-generated DPoP =
proofs. They'll get the refresh token and a bunch of DPoP proofs for =
both the RS and AS. With those they'll be able to get a fresh AT and use =
it with pre-generated Proofs after the end-user leaves the site. No =
available protection (e.g. RT already rotated) will be able to kick in =
until the end-user opens the page again.<br class=3D""><br class=3D"">OTOH=
 with a hash of the AT in the Proof only direct use remains.<br =
class=3D""><br class=3D"">If what I describe above is something we don't =
want to deal with because of direct use already allowing access to =
protected resources, it's sufficiently okay as is (option #1). However, =
if this scenario, one allowing prolonged access to protected resources, =
is not acceptable, it's option #2.<br class=3D""><br class=3D"">Ad #2a =
vs #2b vs #2c. My pre-emptive answer is #2a, simply because we already =
have the tools needed to generate and validate these hashes. But further =
thinking about it, it would feel awkward if this JWS algorithm driven =
at_hash digest selection wouldn't get stretched to the confirmations, =
when this are placed in a JWT access token, cool - we can do that, but =
when these are put in a basic token introspection response it's =
unfortunately not an option. So, #2b (just use sha-256 just like the =
confirmations do).<br class=3D""><br class=3D"">Best,<br =
class=3D"">Filip<br class=3D""><br class=3D""><br class=3D"">On Wed, 2 =
Dec 2020 at 21:50, Brian Campbell &lt;<a =
href=3D"mailto:bcampbell=3D40pingidentity.com@dmarc.ietf.org" =
class=3D"">bcampbell=3D40pingidentity.com@dmarc.ietf.org</a>&gt; =
wrote:<br class=3D"">There were a few items discussed somewhat during =
the recent interim that I committed to bringing back to the list. The =
slide below (also available as slide #17 from the interim presentation) =
is the first one of them, which is difficult to summarize but kinda =
boils down to how much assurance there is that the DPoP proof was =
'freshly' created and that can dovetail into the question of whether the =
token is covered by the signature of the proof. <br class=3D"">There are =
many directions a "resolution" here could go but my sense of the room =
during the meeting was that the contending options were:<br class=3D""> =
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=E2=80=A2 &nbsp;It's sufficiently okay as =
it is<br class=3D""> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=E2=80=A2 =
&nbsp;Include a hash of the access token in the DPoP proof (when an =
access token is present)<br class=3D""><br class=3D"">Going with #2 =
would mean the draft would also have to define how the hashing is done =
and deal with or at least speak to algorithm agility. Options (that I =
can think of) include:<br class=3D""> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=E2=80=
=A2 2a) Use the at_hash claim defined in OIDC core <a =
href=3D"https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken"=
 =
class=3D"">https://openid.net/specs/openid-connect-core-1_0.html#CodeIDTok=
en</a>. Using something that already exists is appealing. But its hash =
alg selection routine can be a bit of a pain. And the algorithm agility =
based on the signature that it's supposed to provide hasn't worked out =
as well as hoped in practice for "new" JWS signatures <a =
href=3D"https://bitbucket.org/openid/connect/issues/1125/_hash-algorithm-f=
or-eddsa-id-tokens" =
class=3D"">https://bitbucket.org/openid/connect/issues/1125/_hash-algorith=
m-for-eddsa-id-tokens</a><br class=3D""> =
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=E2=80=A2 2b) Define a new claim ("ah", =
"ath", "atd", "ad" or something like that maybe) and just use SHA-256. =
Explain why it's good enough for now and the foreseeable future. Also =
include some text about introducing a new claim in the future if/when =
SHA-256 proves to be insufficient. Note that this is effectively the =
same as how the confirmation claim value is currently defined in this =
document and in RFC8705.<br class=3D""> =
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=E2=80=A2 2c) Define a new claim with its =
own hash algorithm agility scheme (likely similar to how the Digest =
header value or Subresource Integrity string is done).<br class=3D""><br =
class=3D"">I'm requesting that interested WG participants indicate their =
preference for #1 or #2. And among a, b, and c, if the latter. <br =
class=3D""><br class=3D"">I also acknowledge that an ECDH approach =
could/would ameliorate the issues in a fundamentally different way. But =
that would be a distinct protocol. If there's interest in pursuing the =
ECDH idea, I'm certainly open to it and even willing to work on it. But =
as a separate effort and not at the expense of derailing DPoP in its =
general current form. <br class=3D"">&lt;Slide17.jpeg&gt;<br =
class=3D""><br class=3D""><br class=3D"">CONFIDENTIALITY NOTICE: This =
email may contain confidential and privileged material for the sole use =
of the intended recipient(s). Any review, use, distribution or =
disclosure by others is strictly prohibited. &nbsp;If you have received =
this communication in error, please notify the sender immediately by =
e-mail and delete the message and any file attachments from your =
computer. Thank you._______________________________________________<br =
class=3D"">OAuth mailing list<br class=3D""><a =
href=3D"mailto:OAuth@ietf.org" class=3D"">OAuth@ietf.org</a><br =
class=3D"">https://www.ietf.org/mailman/listinfo/oauth<br =
class=3D"">_______________________________________________<br =
class=3D"">OAuth mailing list<br class=3D"">OAuth@ietf.org<br =
class=3D"">https://www.google.com/url?q=3Dhttps://www.ietf.org/mailman/lis=
tinfo/oauth&amp;source=3Dgmail-imap&amp;ust=3D1607592086000000&amp;usg=3DA=
OvVaw3hGaihxAdyXVvzFnVTpc6N<br class=3D""></blockquote><br =
class=3D""></blockquote><br =
class=3D"">_______________________________________________<br =
class=3D"">OAuth mailing list<br class=3D""><a =
href=3D"mailto:OAuth@ietf.org" class=3D"">OAuth@ietf.org</a><br =
class=3D"">https://www.ietf.org/mailman/listinfo/oauth<br =
class=3D""></div></div></blockquote></div><br class=3D""></body></html>=

--Apple-Mail=_A12DDCD6-4B0C-4FB5-B013-D015F7C69B2D--

