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

Philippe De Ryck <philippe@pragmaticwebsecurity.com> Fri, 04 December 2020 12:40 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 D1C173A0C2F for <oauth@ietfa.amsl.com>; Fri, 4 Dec 2020 04:40:10 -0800 (PST)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -2.118
X-Spam-Level:
X-Spam-Status: No, score=-2.118 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_FONT_FACE_BAD=0.001, 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=Kv6ePQni; dkim=pass (2048-bit key) header.d=messagingengine.com header.b=cYYw3kqi
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 YlnhXieb6cJa for <oauth@ietfa.amsl.com>; Fri, 4 Dec 2020 04:40:08 -0800 (PST)
Received: from out1-smtp.messagingengine.com (out1-smtp.messagingengine.com [66.111.4.25]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ietfa.amsl.com (Postfix) with ESMTPS id AB0DD3A0C29 for <oauth@ietf.org>; Fri, 4 Dec 2020 04:40:08 -0800 (PST)
Received: from compute3.internal (compute3.nyi.internal [10.202.2.43]) by mailout.nyi.internal (Postfix) with ESMTP id D99CD5C0151; Fri, 4 Dec 2020 07:40:07 -0500 (EST)
Received: from mailfrontend1 ([10.202.2.162]) by compute3.internal (MEProxy); Fri, 04 Dec 2020 07:40:07 -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=F4MVJYR9FcepTM3pBzBdCEycH8vX6rftzmfggplCFAk=; b=Kv6ePQni4Sla rBPuwbfNuTvUQGCpP8LFlx5nU1DuDyrOk65eS2wLfzKaDpGUjqXvndnFTxttwiTF 87CnU3xWPGGlHVvev4VMI2od8skCIzEJ1C4MGYDN8J4YrSj6XldTUX6h6kM/i6/w dOe5w4xxI8c0wxtSHOWvcxttKXr6MclImC03EkuROn3hZyiGaSCWzEVVN1epD35B bAToQ+Mz9cskTRnIdRYWGxiHUlH55C0mdpTu6yDNr5KQXX47pxeB2FqJB9z7iomx ndNU0vAYLH0A7mQxNmMKPl1OXBSqKD+CslpMdVXFQWRJUOuRQoKQPaqhXnCxAtZ4 RtjgdDolyg==
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=F4MVJY R9FcepTM3pBzBdCEycH8vX6rftzmfggplCFAk=; b=cYYw3kqiVSH64sg3cPcG+Z QWlhMJarJ57hGzLcP7AEksk5q9BfokrfIr+9atHm5AxPpr+Cz1z1czYVmGnOTYBy wGFVPqENQaSGv8G15y76n8753j16LLDeXIDc33xjueCo23DVugkK4t8c95wkGuPm CIL49dSFEdG/BEmtuKuM5xb40darm10AEzewOL02slqa9Wmh6dTULhu4LyynQoRD RoT7aY70gT7PF/sdcBHwVBgNck1MUyfkETOUm1hplC5dozBZiK0IpSAqfwvqDbFj F1ZAkvMTLCTVxJnRon5EDynVoQ/uDriqjXdceuMWf7fcFM0vUSB9B78nb9EMbTsQ ==
X-ME-Sender: <xms:Ji7KX2xLsf_hwLCmQNyQYUEXUHSZLwTcDnzJdHij0SLdH1WTIiviFw> <xme:Ji7KXyRBxp8-OExWGMUwVoYQq7oxIDQMOfqTJyosohVR0gTLNLaLnVSB11PB9uWFZ FoEuKuzyTQlTYnkLg>
X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedujedrudeikedggedvucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhephffktgggufffjgfvfhfosegrtdhmrehhtdejnecuhfhrohhmpefrhhhilhhi phhpvgcuffgvucfthigtkhcuoehphhhilhhiphhpvgesphhrrghgmhgrthhitgifvggssh gvtghurhhithihrdgtohhmqeenucggtffrrghtthgvrhhnpeehleejgeegieefgfeigefg geekleelkeefieehteehkeegtdevuedvudekgfdvjeenucffohhmrghinhepvgigrghmph hlvgdrtghomhdpvgigrghmphhlvgdrtghomhdrrghnpdhhthhtphhsfegrvdhfvgigrghm phhlvgdrtghomhenucfkphepkedurdduieegrdduvdelrdelheenucevlhhushhtvghruf hiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehphhhilhhiphhpvgesphhrrghg mhgrthhitgifvggsshgvtghurhhithihrdgtohhm
X-ME-Proxy: <xmx:Ji7KX4VsgGIZA35218HIzbp5vDhuyREETbpvtq92gqRKUBCLF778-A> <xmx:Ji7KX8gerff1yZuYAmTNSB0uxzBPcy3LvzQi6L5T8AVGpdNUMP6qVw> <xmx:Ji7KX4CPAQvJdG9rGPoTOU-I_sjfrl2fjQfagHNdpNiyh-OMOKEbnQ> <xmx:Jy7KXx47rjk7jxubxIrwm3C7aEJrh5Dd53gTIBZDZJfCqToefjMdNA>
Received: from [192.168.1.47] (d51a4815f.access.telenet.be [81.164.129.95]) by mail.messagingengine.com (Postfix) with ESMTPA id 1FFC524005B; Fri, 4 Dec 2020 07:40:06 -0500 (EST)
From: Philippe De Ryck <philippe@pragmaticwebsecurity.com>
Message-Id: <DE120562-2955-461B-9852-4F0B414B18FA@pragmaticwebsecurity.com>
Content-Type: multipart/alternative; boundary="Apple-Mail=_6E97C9C2-73C4-4B1A-BDEC-F122155E00D0"
Mime-Version: 1.0 (Mac OS X Mail 13.4 \(3608.120.23.2.4\))
Date: Fri, 04 Dec 2020 13:40:03 +0100
In-Reply-To: <1B663AA7-563D-4D25-A408-9ED10FD818AC@forgerock.com>
Cc: Torsten Lodderstedt <torsten=40lodderstedt.net@dmarc.ietf.org>, Brian Campbell <bcampbell=40pingidentity.com@dmarc.ietf.org>, oauth <oauth@ietf.org>
To: Neil Madden <neil.madden@forgerock.com>
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> <CE661132-5D86-4652-B115-E6089E39BC68@pragmaticwebsecurity.com> <1B663AA7-563D-4D25-A408-9ED10FD818AC@forgerock.com>
X-Mailer: Apple Mail (2.3608.120.23.2.4)
Archived-At: <https://mailarchive.ietf.org/arch/msg/oauth/7ZxVQWRDRmudiRe6PYsLKpm_mbg>
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 12:40:11 -0000

> The suggestion to use a web worker to ensure that proofs cannot be pre-computed is a good one I think. (You could also use a sandboxed iframe for a separate sub/sibling-domain - dpop.example.com <http://dpop.example.com/>).

An iframe with a different origin would also work (not really sandboxing, as that implies the use of the sandbox attribute to enforce behavioral restrictions). The downside of an iframe is the need to host additional HTML, vs a script file for the worker, but the effect is indeed the same.

> For scenario 4, I think this only works if the attacker can trick/spoof the AS into using their redirect_uri? Otherwise the AC will go to the legitimate app which will reject it due to mismatched state/PKCE. Or are you thinking of XSS on the redirect_uri itself? I think probably a good practice is that the target of a redirect_uri should be a very minimal and locked down page to avoid this kind of possibility. (Again, using a separate sub-domain to handle tokens and DPoP seems like a good idea).

My original thought was to use a silent flow with Web Messaging. The scenario would go as follows:

1. Setup a Web Messaging listener to receive the incoming code
2. Create a hidden iframe with the DOM APIs
3. Create an authorization request such as “/authorize?response_type=code&client_id=...&redirect_uri=https%3A%2F%example.com&state=...&code_challenge=7-ffnU1EzHtMfxOAdlkp_WixnAM_z9tMh3JxgjazXAk&code_challenge_method=S256&prompt=none&response_mode=web_message”
4. Load this URL in the iframe, and wait for the result
5. Retrieve code in the listener, and use PKCE (+ DPoP if needed) to exchange it for tokens

This puts the attacker in full control over every aspect of the flow, so no need to manipulate any of the parameters.


After your comment, I also believe an attacker can run the same scenario without the “response_mode=web_message”. This would go as follows:

1. Create a hidden iframe with the DOM APIs
2. Setup polling to read the URL (this will be possible for same-origin pages, not for cross-origin pages)
3. Create an authorization request such as “/authorize?response_type=code&client_id=...&redirect_uri=https%3A%2F%example.com&state=...&code_challenge=7-ffnU1EzHtMfxOAdlkp_WixnAM_z9tMh3JxgjazXAk&code_challenge_method=S256”
4. Load this URL in the iframe, and keep polling
5. Detect the redirect back to the application with the code in the URL, retrieve code, and use PKCE (+ DPoP if needed) to exchange it for tokens

In step 5, the application is likely to also try to exchange the code. This will fail due to a mismatching PKCE verifier. While noisy, I don’t think it affects the scenario. 


> IMO, the online attack scenario (i.e., proxying malicious requests through the victim’s browser) is quite appealing to an attacker, despite the apparent inconvenience:
> 
>  - the victim’s browser may be inside a corporate firewall or VPN, allowing the attacker to effectively bypass these restrictions
>  - the attacker’s traffic is mixed in with the user’s own requests, making them harder to distinguish or to block
> 
> Overall, DPoP can only protect against XSS to the same level as HttpOnly cookies. This is not nothing, but it means it only prevents relatively naive attacks. Given the association of public key signatures with strong authentication, people may have overinflated expectations if DPoP is pitched as an XSS defence.

Yes, in the cookie world this is known as “Session Riding”. Having the worker for token isolation would make it possible to enforce a coarse-grained policy on outgoing requests to prevent total abuse of the AT.

My main concern here is the effort of doing DPoP in a browser versus the limited gains. It may also give a false sense of security. 



With all this said, I believe that the AS can lock down its configuration to reduce these attack vectors. A few initial ideas:

1. Disable silent flows for SPAs using RT rotation
2. Use the sec-fetch headers to detect and reject non-silent iframe-based flows

For example,  an OAuth 2.0 flow in an iframe in Brave/Chrome carries these headers:
sec-fetch-dest: iframe
sec-fetch-mode: navigate
sec-fetch-site: cross-site
sec-fetch-user: ?1


Philippe