DPoP, PAR and Authorization Code binding

Issue #503 resolved
Dave Tonge created an issue

I have a few questions about DPoP and PAR In FAPI 2

The DPoP spec says this:

Both mechanisms (proof in header or dpop_jkt in body) MUST be supported by an authorization server that supports PAR and DPoP. If both mechanisms are used at the same time, the authorization server MUST reject the request if the JWK Thumbprint in dpop_jkt does not match the public key in the DPoP header.

Do we need to call this out in the FAPI 2.0 spec as I feel it could be missed as we only refer to DPoP as a mechanism for sender-constraining tokens. Whereas it looks like an AS has a requirement to implement DPoP at the PAR endpoint also in order to bind authorization codes.

My reading of the DPoP spec is that this is optional for Clients. Are we happy with this, or should we make it mandatory for Clients?

The DPoP spec says

Use of the dpop_jkt authorization request parameter is OPTIONAL. Note that the dpop_jkt authorization request parameter MAY also be used in combination with PKCE [RFC7636], which is recommended by [I-D.ietf-oauth-security-topics] as a countermeasure to authorization code injection.

Comments (19)

  1. Joseph Heenan

    Discussed a bit on today’s call.

    Brian didn’t believe it would add an benefit to require clients to send it (in the FAPI use case of a confidential client with asymmetric authentication).

    The conformance tests should try sending valid values (as well as not sending it) as clients may send.

    Technically servers are required to support it so we could do negative conformance tests as well; as the most valuable test would consume an authorization code it’s an “expensive” test (when testing banks, the tester generally has to go through MFA authentication for every attempt to obtain an authorization code, and the authorization code may be consumed by a negative test). I wasn’t sure there was enough value to justify that extra hassle for testers.

  2. Brian Campbell

    I’d lean towards not calling it out in FAPI2. It’s subjective but where do you draw the line between repeating or noting specifics of referenced specs and just referencing them?

    Also lean towards keeping it optional for Clients. In FAPI2 clients are using asymmetric client auth and PKCE so I don’t think DPoP binding authorization codes adds enough to justify requiring it.

  3. Filip Skokan

    I’d lean towards not calling it out in FAPI2. It’s subjective but where do you draw the line between repeating or noting specifics of referenced specs and just referencing them?

    We decided to go the other way for dpop-nonce.

    I’m fine either way, but applied to both dpop_jkt and dpop-nonce provisions consistently.

  4. Dave Tonge reporter

    For DPoP-nonce we added a requirement for Clients to support this to aid interoperability - it wasn’t a clear requirement in the main DPoP text for Clients to support the nonce, although it is implied.

    In this scenario, there is already a clear requirement for the AS to support both approaches, so I’m happy to not call it out.

  5. Pieter Kasselman

    Including the DPoP nonce mitigates against proof pre-computation attacks and also removes the need for clock synchronisation (which can be tricky at scale with mobile devices).

    The dpop_jkt authorization request parameter is used to prevent an authorization code re-binding attack. The typical vector for this is attackers accessing log files (e.g. the authorization code ends up in a log file and gets exfiltrated/re-used and the attacker binds their own DPoP key to it). There is a couple of variations of this, including cases where the PKCE code verifier gets harvested along with the Authorization Code from a log file.

    How is the rebinding attack avoided when using PAR, with asymmetric client authentication (i.e. can the authorization code only be used by the same client to whom the authorization code was issued)?

  6. Joseph Heenan

    Thanks for the explanation Pieter!

    How is the rebinding attack avoided when using PAR, with asymmetric client authentication (i.e. can the authorization code only be used by the same client to whom the authorization code was issued)?

    Yes, the authorization code is only usable by the client that it was issued to. (The FAPI conformance tests confirm this mechanism is implemented by attempting to use an authorization code with a different client.)

    If I understand that attack vector correctly, this isn’t necessarily sufficient to prevent that attack if using private_key_jwt and the client authentication assertion was put in the log file and accessed by the attacker, though perhaps I’ve not understood which log file is being talked about?

    I think our current attacker model assumes the PKCE code verifier can’t be harvested though, is that harvesting method applicable to confidential clients?

    I think, with the attacker assumptions you’re making, dpop_jkt may provide extra protection in the FAPI case, at least when considered a per-instance registered confidential client on a mobile device.

  7. Brian Campbell

    My understanding of authorization code re-binding was that it was predicated on the authorization code, PKCE verifier, and client secret being leaked somehow (like a log file) and the AS not strictly enforcing one-time use of the authorization code. A private_key_jwt could be reused too in that situation, if the AS isn’t enforcing one-time use on those (which is optional). A leaked DPoP proof potentially could be reused as well depending on AS implementation around nonce and jti so I don’t think even dpop_jkt fully closes it off.

    There are a number of things that have to happen for it to even be possible. Which is part of why I’d previously said that I don’t think DPoP binding authorization codes adds enough to justify requiring it.

    Note also that there’s no equivalent to dpop_jkt in OAuth MTLS. So I believe authorization code re-binding to a certificate would be possible with similar preconditions of leakage and non enforcement of one authorization code time use and private_key_jwt client auth. I’m not suggesting that needs to be addressed - only that similar situations exist in other combinations of options from FAPI.

  8. Pieter Kasselman

    @Joseph Heenan can you clarify what you mean by “per-instance registered confidential client”?

    Going back to the original reason for including the authorization code binding options in DPoP, the purpose was to prevent the attacker from obtaining all the artefacts it needed from a log file (PKCE verifier and authorization code) and public client (e.g. reverse engineer shared client secret) and then generate a DPoP key under their own control and then use the authorization code to obtain an access token bound to the key they controlled. This “log file” attack removed the need for the attacker to exfiltrate a (potentially ephemeral) DPoP key from the client. So in effect you could perform the authorization code re-binding attack without accessing the client instance under attack (obtain client secret by reverse engineering any instance of the public client and harvest other artefacts from log files).

    From my understanding of FAPI, getting hold of the client secret should be harder due to the use of asymmetric keys by confidential clients. To execute the authorization code re-binding attack, the attacker would need to obtain the client keys, and this would need to be done for a specific client (please correct me if I am overestimating what to expect here). Once the attacker can start accessing client secrets from confidential clients, they can probably obtain DPoP keys (possibly even ephemeral ones). If there is a requirement for confidential client secrets to be hardware bound, it gets even harder to execute.

    I agree that the attacker could still harvest additional artefacts like the authentication assertion, but would now need to be much more active in controlling the channel to maintain the re-binding attack (including constantly harvesting the authentication assertions), but the risk of being detected and complexity goes up significantly. As Brian noted, other pre-conditions would also need to be met, like the “not strictly enforcing one-time use” (a practical reality for large scale deployments).

    So, in summary I think:

    1. The use of confidential clients raises the bar for attackers and reduces the need for using the jkt_dpop parameter
    2. The use of the jkt_dpop parameter may still provide an additional layer of defence so we don’t want to preclude the use of the dpop_jkt parameter.
    3. We can leave use of the dpop_jkt parameter as optional
    4. It may be enough to reference back to the DPoP spec (where it is optional as well, similar to nonces)
    5. If we want to call attention to the dpop_jkt parameter, we should provide guidance on when it is appropriate to select

      1. Only use the dpop_jkt if you are worried about confidential client secrets being compromised and you want to prevent authorization code rebinding.)
      2. The note should also clarify that if the attacker can compromise/access/exfiltrate a confidential client secret, it may be easier for the attacker to just exfiltrate a DPoP key, rather than trying to execute an authorization code re-binding attack.

  9. Joseph Heenan

    @Joseph Heenan can you clarify what you mean by “per-instance registered confidential client”?

    A native mobile app that does dynamic client registration after it’s been installed on a particular device. So each device the app is running on has it’s own client_id and it’s own private key (usually protected in the on device HSM, e.g. iOS secure enclave). [As opposed to the alternative architectures of the native app being a public client, or having a backend holds the OAuth client authentication key.]

  10. Pieter Kasselman

    Thanks @Joseph Heenan . Based on thatdescription, I think you would need to either (1) exfiltrate the client secret for a specific client or (2) manage an interactive attack that harvest authentication assertions for a specific client, which would be harder than then original log file attacks which did not have an ongoing interactive part - once you re-bound the authorization code, you could exfiltrate the token and re-use at will. Authroization code binding will prevent some additional level of protection against these attacks, but they do appear to be harder to execute, so making it optional with guidance on when to consider using it may be sufficient. The argument for making it mandatory is that if we want the most secure profile without requiring implementor judgement, we could just make it manfdatory, but as Brian points out, there may be other incongruencies that arise from that.

  11. Nat Sakimura

    Just copying over the text from the security researchers (via Brian’s chat message on the Call on 2022-08-24)

    "DPoP Authorization Code Binding. Section 10 of the DPoP draft [7] defines an optional dpop_jkt authorization

    parameter. Section 10.1, however, can be read as if an AS supporting PAR [20] and DPoP is required to also support the

    dpop_jkt authorization parameter. As FAPI 2.0 ASs will, in some cases, support both PAR and DPoP, it may be helpful to

    clarify this."

  12. Joseph Heenan

    On today’s call I think we agreed to add text calling out that this is mandatory for AS to implement (as the dpop RFC makes it mandatory if par is supported, which FAPI2 requires).

    As it does have additional value in the FAPI2 case (as discussed above), we’re currently planning to implement tests for it in the FAPI2 conformance suite as per https://gitlab.com/openid/conformance-suite/-/issues/1038

  13. Log in to comment