Re: [OAUTH-WG] DPoP followup I: freshness and coverage of signature

Philippe De Ryck <philippe@pragmaticwebsecurity.com> Fri, 04 December 2020 09:22 UTC

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

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). 

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 = lots of proofs), but does not really strengthen the defense beyond existing mitigations (Short AT lifetimes and RT rotation). 



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. 

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. 

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. 

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. 

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. 

In a nutshell: XSS = Game over


—
Pragmatic Web Security
Security for developers
https://pragmaticwebsecurity.com/


> On 3 Dec 2020, at 13:55, Torsten Lodderstedt <torsten=40lodderstedt.net@dmarc.ietf.org> wrote:
> 
> I understand. Thanks! 
> 
> I think RT rotation + RT hash in the proof would also stop the attack.  
> 
>> Am 03.12.2020 um 13:19 schrieb Filip Skokan <panva.ip@gmail.com>om>:
>> 
>> I'm failing to understand why binding the proof to the access token ensures freshness of the proof.
>> 
>> 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.
>> 
>> S pozdravem,
>> Filip Skokan
>> 
>> 
>> On Thu, 3 Dec 2020 at 12:59, Torsten Lodderstedt <torsten@lodderstedt.net> wrote:
>> Hi, 
>> 
>> 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. 
>> 
>> 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. 
>> 
>> best regards,
>> Torsten. 
>> 
>>> Am 03.12.2020 um 10:20 schrieb Filip Skokan <panva.ip@gmail.com>om>:
>>> 
>>> Hi Brian, everyone,
>>> 
>>> 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.
>>> 
>>> OTOH with a hash of the AT in the Proof only direct use remains.
>>> 
>>> 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.
>>> 
>>> 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).
>>> 
>>> Best,
>>> Filip
>>> 
>>> 
>>> On Wed, 2 Dec 2020 at 21:50, Brian Campbell <bcampbell=40pingidentity.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. 
>>> There are many directions a "resolution" here could go but my sense of the room during the meeting was that the contending options were:
>>>      •  It's sufficiently okay as it is
>>>      •  Include a hash of the access token in the DPoP proof (when an access token is present)
>>> 
>>> 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:
>>>      • 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
>>>      • 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.
>>>      • 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).
>>> 
>>> I'm requesting that interested WG participants indicate their preference for #1 or #2. And among a, b, and c, if the latter. 
>>> 
>>> 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. 
>>> <Slide17.jpeg>
>>> 
>>> 
>>> 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=https://www.ietf.org/mailman/listinfo/oauth&source=gmail-imap&ust=1607592086000000&usg=AOvVaw3hGaihxAdyXVvzFnVTpc6N
>> 
> 
> _______________________________________________
> OAuth mailing list
> OAuth@ietf.org
> https://www.ietf.org/mailman/listinfo/oauth