[OAUTH-WG] Re: Browser-Swapping

"Primbs, Jonas" <jonas.primbs@uni-tuebingen.de> Wed, 05 November 2025 03:55 UTC

Return-Path: <jonas.primbs@uni-tuebingen.de>
X-Original-To: oauth@mail2.ietf.org
Delivered-To: oauth@mail2.ietf.org
Received: from localhost (localhost [127.0.0.1]) by mail2.ietf.org (Postfix) with ESMTP id D7BDB832D5E8 for <oauth@mail2.ietf.org>; Tue, 4 Nov 2025 19:55:15 -0800 (PST)
X-Virus-Scanned: amavisd-new at ietf.org
X-Spam-Flag: NO
X-Spam-Score: -4.398
X-Spam-Level:
X-Spam-Status: No, score=-4.398 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_DNSWL_MED=-2.3, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_PASS=-0.001] autolearn=ham autolearn_force=no
Authentication-Results: mail2.ietf.org (amavisd-new); dkim=pass (2048-bit key) header.d=uni-tuebingen.de
Received: from mail2.ietf.org ([166.84.6.31]) by localhost (mail2.ietf.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id lmvCks5lU5Tb for <oauth@mail2.ietf.org>; Tue, 4 Nov 2025 19:55:14 -0800 (PST)
Received: from mx03.uni-tuebingen.de (mx03.uni-tuebingen.de [134.2.5.213]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-256) server-digest SHA256) (No client certificate requested) by mail2.ietf.org (Postfix) with ESMTPS id 6BE28832D5DE for <oauth@ietf.org>; Tue, 4 Nov 2025 19:55:14 -0800 (PST)
Received: from exchange.uni-tuebingen.de (ex01.uni-tuebingen.de [134.2.21.161]) by mx03.uni-tuebingen.de (Postfix) with ESMTPS id 737F220A6AFC; Wed, 5 Nov 2025 04:55:07 +0100 (CET)
DKIM-Filter: OpenDKIM Filter v2.11.0 mx03.uni-tuebingen.de 737F220A6AFC
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=uni-tuebingen.de; s=20211202prod; t=1762314907; bh=hTKp9nYRPCgB5CxC6GAVjsyPV4MkciLztuGNuvfwf80=; h=From:To:CC:Subject:Date:References:In-Reply-To:From; b=Qna4h6qVl/KPvyFu+G+KYJHJerrS8teqK67Fk3LZb4mqqRRs702GvsuK6PvxBJ6BC cnm0nUy23mf06Zk50MZ/VBezxFtmAoqOysPsaKPawIxM4dzy3rlNz0ddS6iWjAQ3bN CGFwi1lGnRBnxRY5x872TELSZ/utaYiWOr8ky1LTmiaPItlmCXGekoweWRTkPQHNNR iCURqtT3daPzZGLiD83jStd4Fb6mDuzq77hoKL70o2aiwQLIwwXvfQ9j3NMzrrqnlm SGRKdi5kUfZVfNQInuvza6a0cZjDAs0KT8N7VG0lavDtT+SryUTZrea8I0WkCJI5ck 5gRjobjy+ZFDQ==
Received: from Ex02.uni-tuebingen.de (134.2.21.162) by EX01.uni-tuebingen.de (134.2.21.161) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.61; Wed, 5 Nov 2025 04:55:07 +0100
Received: from Ex02.uni-tuebingen.de ([fe80::5ddd:1152:31fc:6bd3]) by EX02.uni-tuebingen.de ([fe80::5ddd:1152:31fc:6bd3%7]) with mapi id 15.01.2507.061; Wed, 5 Nov 2025 04:55:06 +0100
From: "Primbs, Jonas" <jonas.primbs@uni-tuebingen.de>
To: Neil Madden <neil.e.madden@runbox.eu>
Thread-Topic: [OAUTH-WG] Browser-Swapping
Thread-Index: AQHcTUDUzxxd8Ix3dU6emBb6CO2LGbTiLP6AgABDLgCAABoEgIAA2nAA
Date: Wed, 05 Nov 2025 03:55:06 +0000
Message-ID: <3A986190-DEC4-4BDD-911E-E2324B39A55E@uni-tuebingen.de>
References: <F032F35A-D55E-40A7-8589-3DD64BF8F7A0@uni-tuebingen.de> <E29795F9-8642-4DCE-A539-410E0BD6DAB5@runbox.eu> <FDE0C917-9F2F-4FAD-A4E5-7FA187E2EB3A@uni-tuebingen.de> <F042D0C8-421C-4FA2-B373-1BD254C05ED3@runbox.eu>
In-Reply-To: <F042D0C8-421C-4FA2-B373-1BD254C05ED3@runbox.eu>
Accept-Language: de-DE, en-US
Content-Language: en-US
X-MS-Has-Attach: yes
X-MS-TNEF-Correlator:
x-mailer: Apple Mail (2.3864.100.1.1.5)
x-originating-ip: [134.2.21.181]
Content-Type: multipart/signed; boundary="Apple-Mail=_BBBF0B2F-A20F-4042-ADAC-A9462FEF9301"; protocol="application/pkcs7-signature"; micalg="sha-256"
MIME-Version: 1.0
Message-ID-Hash: KAVJRM5W6JU2HQGHE3TYDEIJZLJWKPMQ
X-Message-ID-Hash: KAVJRM5W6JU2HQGHE3TYDEIJZLJWKPMQ
X-MailFrom: jonas.primbs@uni-tuebingen.de
X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-oauth.ietf.org-0; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header
CC: oauth <oauth@ietf.org>
X-Mailman-Version: 3.3.9rc6
Precedence: list
Subject: [OAUTH-WG] Re: Browser-Swapping
List-Id: OAUTH WG <oauth.ietf.org>
Archived-At: <https://mailarchive.ietf.org/arch/msg/oauth/FnNVgVPcFYMDJShTtDY8ByxtISA>
List-Archive: <https://mailarchive.ietf.org/arch/browse/oauth>
List-Help: <mailto:oauth-request@ietf.org?subject=help>
List-Owner: <mailto:oauth-owner@ietf.org>
List-Post: <mailto:oauth@ietf.org>
List-Subscribe: <mailto:oauth-join@ietf.org>
List-Unsubscribe: <mailto:oauth-leave@ietf.org>

Hi Neil,

> Am 04.11.2025 um 09:53 schrieb Neil Madden <neil.e.madden@runbox.eu>:
> 
> 
> 
>> On 4 Nov 2025, at 13:37, Primbs, Jonas <jonas.primbs@uni-tuebingen.de> wrote:
>> 
>> Hi Neil,
>> 
>> thanks for your valuable feedback.
>> You will find my thoughts inline.
>> 
>>> Am 04.11.2025 um 04:19 schrieb Neil Madden <neil.e.madden@runbox.eu>:
>>> 
>>> Thanks for sharing your slides. Although this is an interesting theoretical attack, I do not think it is much of a concern in practice. If we take your attack vectors from slide 12:
>>> 
>>> * Referer header leaks: the default policy in browsers since 2019 is strict-origin-when-cross-origin, which prevents this leak [1].
>> 
>> I still think it is worth to mention in the draft because even if it’s the default, it does not mean that client implementors do not change it in a bad way.
>> Moreover, in modern infrastructures, reverse proxies are often used for path-based routing, which results resources (images, favicons etc) being referenced on the same host but requests will be routed to external parties like CDNs.
>> 
>>> * URL sharing and analytics tools are already addressed by [2] in the original OAuth RFC 6749, which already says to avoid 3rd party analytics on the redirect endpoint and to redirect immediately after collecting the credentials. I think most providers do in fact do this?
>> 
>> Yes, I have never seen the issue with the analytics tools in the wild before, I just added it for completeness.
>> Out of the last 10 oauth client penetration tests, of which 6 were affected by the browser swapping attack (the other 4 did not even implement the CSRF protection), only one was affected by the URL sharing.
>> However, redirecting to another URL, not including scripts, and ensuring that referrer-policies are applied correctly are all mitigation strategies, but not solve the general issue of sending a secret (the authorization code) as a query parameter, which is a bad practice in general [1].
>> 
>>> That leaves leakage via logs, which IMO is adequately addressed by (a) use of TLS (minimising on-path observers) and (b) using short-lived auth codes. 
>> 
>> a) That’s correct.
>> b) According to RFC 6749, the maximum validity period is 10 minutes. Today, many logging happens in real-time, so my impression was that 10 minutes is enough for attackers with access to logs. Think of a shell script which polls every minute for the logs and greps for the keyword „code“, extracts the authorization code and automatically resolves that.
> 
> Attackers with real-time access to logs are generally already quite privileged (eg they have ssh access to the servers or access to a SIEM). The risk from logs mostly comes from them being copied into support tickets or archived to insecure S3 buckets and similar things. Those things don't generally happen in 10 minutes.

I don’t agree here.
Having read-only access to a SIEM system, which provides you real-time access to logs being collected from a client application or any involved reverse proxy, load balancer, middleware etc, should not enable you taking over the client session of any user.
This is a behavior, which many service providers will not expect from an OAuth-compliant software.

Moreover, this increases the criticality of misconfigured HTTP servers, e.g., an NGINX or Apache web server which allow you to access the server’s logs in real-time by browsing something like /../../../../var/logs/nginx/access.log
I agree, such misconfigurations should not be in scope of OAuth. However, this increases the criticality of such a finding from an information leak to a medium or even high - and OAuth could improve that by slightly changing the specs.

>> 
>>> Using fragments or form_post both have significant downsides. Fragments only work with Javascript, which introduces new failure cases and attack surface (ideally the redirect endpoint would be served with CSP script-src=none). 
>> 
>> Yes, that’s why I recommend this for web apps running in the user’s browser with javascript anyways, or for mobile apps.
> 
> Re-reading my old blog post (https://neilmadden.blog/2019/01/16/can-you-ever-safely-include-credentials-in-a-url/) reminds me of a drawback of using the fragment: if you don't explicitly clear it, then it is carried over on redirects, potentially leaking the auth code to other pages/sites. (At least, that used to be the case - I don't believe that behaviour has changed).

Yes, but we have the same issue with query parameters too, right?
Fragment however has the advantage of reducing the attack surface with respect to logging from an entire landscape down to the client.

> 
>> 
>>> Form post requires use of samesite=none cookies, weakening CSRF protections. I don’t think we should be recommending weakening protections against very real threats (XSS, CSRF) to protect against something that seems unlikely. 
>> 
>> Thanks a lot for that hint with the CSRF protection with samesite=none.
>> I think samesite=none works perfectly fine, if the „session“ cookie is not a real session cookie, but the state parameter instead, because then the „state“ cookie, which contains the state parameter combined with the identical state parameter reflected by the AS in the POST body parameter apply the double submit cookie pattern [2], which is also a well-known CSRF protection mechanism.
>> The real session cookie (if you even need one before you are logged in) could still use samesite=strict or lax.
>> But I get the point that maybe I should mention this in the draft.
>> 
>>> We can perhaps improve the wording around existing countermeasures if there is evidence they are being ignored, but I think that is enough. I could be persuaded otherwise, but I’d need to see more evidence that this is a problem in practice and that the countermeasures do more good than harm. 
>> 
>> I think the problem with response_mode=query in general is that the authorization code is transferred in the URL which opens up many attack vectors that all need to be fixed.
>> The most critical one are logs because there is no general fix for that, exept not providing the authorization code as a query parameter.
>> My impression was that our customers were really surprised that this is still the default behavior of OAuth and they didn’t expect that from such a big large-scale deployed standard.
>> Especially our customers, which just deploy existing client implementations, did not expect that they have to care that much about logs of, e.g., their reverse proxies, and they were shocked that these logs which are sometimes audited by external partners enable the log auditor to start a browser-swapping attack and take over, e.g., administrator accounts.
> 
> I agree in general, but (a) the situation is a lot less bad than it used to be and (b) the short-lived nature of auth codes does IMO mitigate a lot of these issues. According to https://www.oauth.com/oauth2-servers/authorization/the-authorization-response/, most auth codes are in fact only valid for 30–60 seconds, which would further reduce the opportunity to exploit this. 

That’s right, maybe we should recommend a validity period of 1 instead of 10 minutes in OAuth 2.1.

By the way, Aaron and I had a discussion on that today and we figured out that the „implicit flow“ of OIDC might also be affected. Probably things are much more critical here than in OAuth.

We also discovered an alternative fix for that by enforcing PKCE for every flow and sending a token request from client to AS even if state parameters don’t match.
This promises to simplify the specs in general with the nice side effect that it also sufficiently fixes browser-swapping attacks without breaking changes.
I will discuss that with Aaron and Mike and will send an update.

> I'm sympathetic to the idea that query mode is not great, but it's pretty well established at this point and we've spent a long time telling people to use it. I don't find this a compelling enough reason to suggest a change. 

I get your point. However, I’m not a friend of leaving things as they are, just for not having to correct previous statements, especially if we could do better.

>> I totally agree, that we cannot simply deprecate response_mode=query for legacy reasons. However, I think we should ensure that client developers and providers are aware of the risk that authorization codes can get logged and can be used to take over user sessions.
>> 
>> [1] https://owasp.org/www-community/vulnerabilities/Information_exposure_through_query_strings_in_url
>> [2] https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#alternative-using-a-double-submit-cookie-pattern
> 
> — Neil

Independently of whether browser-swapping attacks are a realistic attack or not — does anyone disagree with adding or at least referencing response modes in the OAuth 2.1 spec in general?
I personally would have been glad to have had any reference to them from another document instead of being lucky that I accidentially stumbled over this spec.

Greetings,
Jonas