Re: [OAUTH-WG] OAuth 2.0 for Browser-Based Apps - On the usefulness of refresh token rotation

Philippe De Ryck <philippe@pragmaticwebsecurity.com> Sat, 16 May 2020 18:21 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 75ADF3A0062 for <oauth@ietfa.amsl.com>; Sat, 16 May 2020 11:21:37 -0700 (PDT)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -0.196
X-Spam-Level:
X-Spam-Status: No, score=-0.196 tagged_above=-999 required=5 tests=[DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_BLOCKED=0.001, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, 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=CqirIaAr; dkim=pass (2048-bit key) header.d=messagingengine.com header.b=Mc7NlDcg
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 dZF6HZ5LtLUd for <oauth@ietfa.amsl.com>; Sat, 16 May 2020 11:21:35 -0700 (PDT)
Received: from wout5-smtp.messagingengine.com (wout5-smtp.messagingengine.com [64.147.123.21]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ietfa.amsl.com (Postfix) with ESMTPS id 6D99C3A0061 for <oauth@ietf.org>; Sat, 16 May 2020 11:21:35 -0700 (PDT)
Received: from compute2.internal (compute2.nyi.internal [10.202.2.42]) by mailout.west.internal (Postfix) with ESMTP id 2D888434; Sat, 16 May 2020 14:21:34 -0400 (EDT)
Received: from mailfrontend2 ([10.202.2.163]) by compute2.internal (MEProxy); Sat, 16 May 2020 14:21:34 -0400
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=fm1; bh=qUR3g42KCDlNRdDHCgXSWMti2RIL2cufFBVTc6Kf/Aw=; b=CqirIaArwp1z /knVRisceSfcrJ0x3j4xhoY5kxoaoF+1RxivE/kxO+swaeCg4J+BYE8+th08uGIG H/3r8RECwu+qcvLQ0fAPg/ReMdxVQOzlQmNLdvhxlznuu7ZnV16t4hXCDgbtkUyU PSjjbOKr7XbWCD/Dnk2Scs8l1Cqlqp7VhkA76BB8+FtRrMBpYlzP+yEP958Bc52r 152P9yyW9HCKHcTb6CdwqvBYSnyFOhO1P4rI2BV++1QsZIEGsm5WZnoBGC6R0oO5 rEDMLbcl0gpf35UsTYrT4TAwm5+fJpStvHAnswmmQfTwEcTP0sY/V1as03KyhicY FB+wB4opQg==
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=fm2; bh=qUR3g4 2KCDlNRdDHCgXSWMti2RIL2cufFBVTc6Kf/Aw=; b=Mc7NlDcgF3PJtute8YCXis W+H2YF9rG4N8HEasQoqO14yT4K2PV/oY+0Gbkii9ba/nrHp+KvXYWOjc5Xz5za9y QqoNPXR0A958Q0XmYCmMXAgLZgBiDNVKR/DeOprKqXQM8opqQbNLqUnRUmZ10412 f0fihwKsyMSD5GC0gaYHNJkYj+DHpVg2F+NMf185QSDW844XCuM67wqmek+DbfbT opKwOmq1Tiz9LS7ut5BXBZmmvi4WzGRigbbSRzE/YVn6ujjhL4GRMyM+iyBXqTDy 2asai0sCRsYF+7e9kWT7CyV+UEVQKJkqD5ps8d/MhfOxXY4oJzxptOET90lDer4g ==
X-ME-Sender: <xms:LS_AXkNSpF89KjV8TYsG_2LcZrs_RiMUzZOjzItKx2mY7yeUPUGpWA>
X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeduhedruddttddguddvvdcutefuodetggdotefrod ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfgh necuuegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmd enucfjughrpefhkfgtggfuffgjvfhfofesrgdtmherhhdtjeenucfhrhhomheprfhhihhl ihhpphgvucffvgcutfihtghkuceophhhihhlihhpphgvsehprhgrghhmrghtihgtfigvsg hsvggtuhhrihhthidrtghomheqnecuggftrfgrthhtvghrnhepgedvueegvefhvedvjedv iedtheekteegtdffffdvueejheevuddtffeglefgvdeunecuffhomhgrihhnpehmohiiih hllhgrrdhorhhgpdhivghtfhdrohhrghenucfkphepkedurdduieegrdduvdelrdelheen ucevlhhushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehphhhilh hiphhpvgesphhrrghgmhgrthhitgifvggsshgvtghurhhithihrdgtohhm
X-ME-Proxy: <xmx:LS_AXq9UmZK90Mc7s6OyK9V3VnMQUoGGukE-eHEhYRceOHvUTq9WVQ> <xmx:LS_AXrSgEvgjIeptuwZdZCWji-BiMrFXBHq9XbfAVkS6MoCWaphVFA> <xmx:LS_AXsuQI9_M7W_NDhc08dKvfFy1gYYaYIeYBykU3cRNyGch6Kq5mQ> <xmx:LS_AXmqiRXCjUaoIYTH9cgNp96Hyxw8fr6nCtja_6HYS1Aw0VDdidA>
Received: from imacvanphilippe.localdomain (d51a4815f.access.telenet.be [81.164.129.95]) by mail.messagingengine.com (Postfix) with ESMTPA id E0486306638F; Sat, 16 May 2020 14:21:32 -0400 (EDT)
From: Philippe De Ryck <philippe@pragmaticwebsecurity.com>
Message-Id: <C9ED4362-80D1-4D0C-A6A0-8CBF51A6EAD7@pragmaticwebsecurity.com>
Content-Type: multipart/alternative; boundary="Apple-Mail=_4244A1A6-4EA0-4057-97E4-DD46BD8C891E"
Mime-Version: 1.0 (Mac OS X Mail 13.4 \(3608.80.23.2.2\))
Date: Sat, 16 May 2020 20:21:31 +0200
In-Reply-To: <4A9DFEB7-9503-4034-9F33-4B2DC399CE4F@lodderstedt.net>
Cc: oauth <oauth@ietf.org>
To: Torsten Lodderstedt <torsten@lodderstedt.net>
References: <CA0EA922-5997-4BDE-8C47-D4EDBAC99B13@pragmaticwebsecurity.com> <4A9DFEB7-9503-4034-9F33-4B2DC399CE4F@lodderstedt.net>
X-Mailer: Apple Mail (2.3608.80.23.2.2)
Archived-At: <https://mailarchive.ietf.org/arch/msg/oauth/j-k3jVzWZkeVajyAdRaSH6fuLbo>
Subject: Re: [OAUTH-WG] OAuth 2.0 for Browser-Based Apps - On the usefulness of refresh token rotation
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: Sat, 16 May 2020 18:21:37 -0000

Hi Torsten,

> On 16 May 2020, at 19:50, Torsten Lodderstedt <torsten@lodderstedt.net> wrote:
> 
> Hi Philippe, 
> 
>> On 16. May 2020, at 17:08, Philippe De Ryck <philippe@pragmaticwebsecurity.com> wrote:
>> 
>> Hi all,
>> 
>> I am working on formulating developer guidelines on using Refresh Token Rotation (RTR), as required by "OAuth 2.0 for Browser-Based Apps". 
>> 
>> The protection offered by RTR kicks in the moment a refresh token is used twice, so the assumption is that the attacker has the ability to steal tokens from the client. In general, this means the attacker has malicious code running in the application (e.g., XSS, remote JS inclusion, ...). 
>> 
>> Within these constraints, I can think of a couple of malicious payloads that sidestep the protection offered by RTR:
>> 
>> 1. Stealing access tokens in an online XSS attack
>> 2. Stealing refresh tokens, but waiting to use the latest until the original client is no longer active
>> 3. Running a silent authentication flow in an iframe to obtain a new and unrelated AT and RT, and use that until it expires
>> 
>> Scenario 1 is straightforward in most applications, but the attack requires the vulnerable application to remain online. Scenario 2 might be difficult if the RT is kept out of reach from the main application (e.g. in a worker thread). Scenario 3 is most dangerous, but also a bit tricky to implement as the payload needs to make sure the application's code does not interfere (however, the browser's Same-Origin Policy will not intervene). The specifics depend on the concrete implementation, but all three attacks are technically feasible.
>> 
>> With these attacks in mind, it seems that the use of the Authorization Code flow with RTR does not really add much improvement for security, if other best practices are followed (e.g., using HTTPS). RTR does a lot for usability and handling third-party cookie blocking scenarios though.
> 
> I also see this as the main advantage of RTs.
> 
> I think scenario 3 can be made more difficult for the attacker by requiring user interaction. That’s ok since the normal case would be to refresh via RT and not via authorization flow, so the legit app shouldn’t be affected. 

Preventing a silent flow from happening would indeed stop this attack vector, but it might create usability problems in single page applications.

A typical scenario is an SPA running a silent authentication flow in an iframe when it is first started. This allows the app to bootstrap itself with the user’s authentication status if a session already exists. This pattern is common when tokens are kept in memory, as a simple page reload causes that state to be cleared. Since Safari and Brave already block third-party cookies, they cannot run a silent flow in an iframe. A workaround would be to run a top-level silent redirect-based flow to check if an authenticated session exists or not. The impact on the UX for this initial redirect is limited, since it is silent anyway. By turning off a silent flow, both use cases would stop working. 

I totally get that this is a quite challenging problem to address. Given your suggestion, the authorization server could prevent iframe-based flows when RTs are used, but still allow top-level navigation flows for the bootstrap phase. Right now, I don’t think we can implement such a detection mechanism in a reliable way, but hopefully the upcoming Sec-Fetch-Dest header can help here (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Dest <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Dest>)

> 
>> 
>> In this context, my advice to developers is to avoid handling tokens in the browser in security-sensitive scenarios. A Backend-for-frontend pattern gives a server-side component control over tokens, along with the ability to implement additional security measures.
> 
> I full agree with those advice. Handling security sensitive aspects of the app out of reach of the user (which might be an attacker) is a good idea. On the functional side, this also gives the app access to authentication and sender constrained access tokens via mTLS.

That’s precisely my recommendation as well. 

>> 
>> Additionally, is there any official recommendation to link the validity of a refresh token to the lifetime of the user's session with the Authorization Server? Having that property gives RTR similar security properties as the silent renew scenario. 
> 
> Section 4.12.2. of the Security BCP recommends refresh token revocation in case of logout. 

Right, I should have checked that more closely for the details. I also see it recommends the timeout after inactivity, which corresponds somewhat to linking it to the session lifetime (which would have similar properties). 

Thanks!

Philippe

> 
> best regards,
> Torsten. 
> 
>> 
>> Any feedback on this train of thought is more than welcome.
>> 
>> Philippe
>> 
>> 
>> _______________________________________________
>> OAuth mailing list
>> OAuth@ietf.org <mailto:OAuth@ietf.org>
>> https://www.ietf.org/mailman/listinfo/oauth <https://www.ietf.org/mailman/listinfo/oauth>