PKCE & Nonce Security Considerations

Issue #293 new
Dave Tonge created an issue

@Daniel Fett has posted a very useful analysis of nonce and PKCE:

https://danielfett.de/2020/05/16/pkce-vs-nonce-equivalent-or-not/

We should consider whether to add additional security considerations around this in FAPI and if so, whether they need to be in part 1 or part 2.

There was discussion on the call today of potentially requiring servers to reject token requests with a code_verifier where none was expected.

There was also discussion about whether in Part 2 we are protected against such attacks due to the integrity protection from JARM or ID Tokens.

We agreed to open this issue for further discussion.

Comments (12)

  1. Francis Pouatcha

    Hello Daniel,

    I do appreciate the ground work done in this paper https://danielfett.de/2020/05/16/pkce-vs-nonce-equivalent-or-not/. Here are some of my remarks:

    The paper shows how complex redirection can be. A pertinent aspect of a secure specification shall therefore be it simplicity. This is why I advocate keeping newer FAPI profile simple by. removing options.

    1. Newer FAPI profiles shall mandate only support PKCE and nothing else?
    2. In order not to expose authZ request to an attacker, pushed authZ request must be mandated for newer FAPI profiles (done).
    3. Event if protectable using PKCE, public client wont be able to satisfy repudiation requirements of payment request (QSeal Signature of request). This is why newer FAPI shall rule out the use of public clients.
    4. For confidential clients, like you mention in a couple of location in this paper, the capability to maintain a sound session between RO-Browser and Client is essential to guaranty that the RO-Browser initiating the authZ-request is the same instance as the RO-Browser that processes the authZ-response and forwards the authZ-code to the confidential client. We will need to introduce the concept of redirect_session for this purpose. This is the RO-Browser state that holds the code_verifier, and returns it to the confidential client with the authZ-response for use at the token endpoint. Off course this redirect_session shall be protected with a csrf_state parameter that will be part of the redirected authZ-response. The confidential client validating the the redirect_session matches the csrf_state will extract the code_verifier from the redirect_session and use if to request the access token from the AS.

    If all of those are already addressed in the FAPI2.0 basic profile, just disregard this message.

  2. Daniel Fett

    Hi Francis,
    thanks for the feedback! These are very good points. 1./2./3. are already mandated in FAPI 2.0 Baseline (and therefore also in FAPI 2.0 Advanced).

    Re 4., I would normally expect that any sane implementation already has a session, maintained via standard web session mechanisms, between the RO-Browser and the Client. The OAuth security BCP also highlights that the PKCE challenge/verifier must be bound securely to this session. This was also the assumption in made in the article. I’m not sure what extra security your proposal would bring (or I don’t understand the proposed mechanism). Could you please explain this a little more?

  3. Francis Pouatcha

    Hello Daniel,

    I am not proposing any new approach. The emphasis on how to maintain a secure session between the RO-Browser and the Client across redirects is generally missing. The problem is very visible in an open banking environment, where oAuth is used not for establishing identity of RO, but for authorizing transactions.

    1. The PSU (RO) has an account with the TPP (Client). There is a web session between the PSU-Browser and the TPP-Server. The web session (PSU2TPP-Session) is generally maintained with a cookie + csrf_token.
    2. When the TPP-Server redirects the PSU-Browser to the ASPSP-AS for authorization of a payment, the context of the TPP-UI-App in the PSU-Browser is replaces with the ASPSP-AS-UI-App. At this moment we have to decide on how to proceed with the PSU2TPP-Session in the PSU Browser. We can either terminate it explicitly or let it expire. Using it to manage the redirect process bears a lot of risk.

      1. Early expiry might lead to the lost of the bound of the PSU-Browser to the TPP-Server. For this reason common implementation out there will set higher session timeout for the PSU2TPP-Session.
      2. Higher session timeout is a risk as PSU might abandon the payment authorization process at the ASPSP-AS-UI-App and have no chance of terminating the long running PSU2TPP-Session. We won’t even have the scripting support used by single page apps to auto terminate session in case of non activity.
    3. The session we need to hold/reference the code_verifier associated with the ongoing authorization process has to be a different one. I call this a REDIRECT_SESSION. This session shall be distinguished from a classical web session (PSU2TPP-Session) in that:

      1. A REDIRECT_SESSION might have a longer life span (expir 5/10/20 mins) that a classical web session (PSU2TPP-Session)(expir 1 mins), as the PSU will need more time to authorize the payment with the ASPSP-AS.
      2. A REDIRECT_SESSION must only be bound a dedicated authorization process, as PSU might initiate parallel payments with different ASPSP, pushing the need of managing parallel running redirect processes. This is a open banking situation we wont witness in classical oAuth/OIDC workflows.
      3. Many single page applications have scripts, that warn the PSU for inactivity and extends the PSU2TPP-Session if necessary. These scripts won’t be of help for the REDIRECT_SESSION as the contest of the TPP-UI-App will be lost with the redirection to the ASPSP-AS.

    My point here is that there is a need to educate the implementor of the TPP-Server (Client) on the importance of this sound REDIRECT_SESSION that must be used hold/reference the code_verifier.

  4. Dave Tonge reporter

    I’m not sure what we are going to do with this ticket? @Daniel Fett do you think we need to add anything to FAPI 1.0

  5. Daniel Fett

    Francis, please correct me if I’m wrong, but I feel like we are discussing a couple of separate problems here:

    • Parallel running OAuth sessions: This can be addressed by using the OAuth state parameter for its original purpose or by storing the session information in the SessionStorage of the browser.
    • Timeout of PSU2TPP session: This can be addressed either by your approach with two different sessions (which is already common in the e-commerce space) or by storing timestamps in the PSU2TPP session and requiring a reauthentication of the user when a certain age of the last login is reached (no need to rely on the built-in timeout of the session or cookie).
    • Ensuring that the browser used to interact with the TPP and the one interacting with the ASPSP are the same: Except for token binding, I’ve not seen a really good solution for this problem yet.

    All in all, this seems like good content for a kind of best practices document.

  6. Francis Pouatcha

    Parallel running OAuth sessions: This can be addressed by using the OAuth state parameter for its original purpose or by storing the session information in the SessionStorage of the browser.

    the purpose of the state parameter shall be solely to protect a corresponding session against CSRF

    Timeout of PSU2TPP session: This can be addressed either by your approach with two different sessions (which is already common in the e-commerce space)

    I prefer the two sessions approach (like you mention above common in the e-commerce space):

    • A psu2tpp session (login_session) that is active while interaction between psu agent and tpp server is active. Corresponding session cookie shall be deleted prior to redirecting the user to the AS.
    • A redirect_session that is active when a redirect is going on. This shall also be set to expected time the psu need to authenticate with the AS and authorize the transaction.

    or by storing timestamps in the PSU2TPP session and requiring a reauthentication of the user when a certain age of the last login is reached (no need to rely on the built-in timeout of the session or cookie).

    It is better to always delete the psu2tpp session prior to redirecting the user to another domain. Keeping this session will require reusing the corresponding state parameter in the redirect process.

    Ensuring that the browser used to interact with the TPP and the one interacting with the ASPSP are the same: Except for token binding, I’ve not seen a really good solution for this problem yet.

    Although this is important, I rather define PSU identity equivalence like this: the PSU interacting with the ASPSP is the same as the PSU interacting with the TPP. Depending on the user device and redirection type, both user agents (PSU ↔︎ ASPSP) and (PSU ↔︎ TPP) might be the same browser instance or not (e.g. browser and native app).

    If the browser used for (PSU ↔︎ TPP) stores a redirect_session prior to redirecting the user to the ASPSP, the url redirecting the PSU from the ASPSP back to the TPP can bring a state parameter that is used to retrieve and unlock the redirect session.

    PKCE:

    If the TPP application is designed to hold the code_verifier in the redirect_session, then you have a sufficient level of assurance that you are dealing with the same PSU on both ASPSP and TPP interface.

  7. Daniel Fett

    the purpose of the state parameter shall be solely to protect a corresponding session against CSRF

    Why would that be the sole purpose of the state parameter? After all, this is closer to the original idea behind state than the CSRF protection is.

  8. Francis Pouatcha

    Why would that be the sole purpose of the state parameter? After all, this is closer to the original idea behind state than the CSRF protection is.

    What is the original idea behind the state parameter?

  9. Daniel Fett

    What is the original idea behind the state parameter?

    Tracking any state between the two invocations of the client when there is no session/cookie to do that. Hence the name. RFC6749 says:

    An opaque value used by the client to maintain state between the request and callback.

    Although it quickly got its second pupose as a CSRF prevention measure.

    For reference, this was the message thread where this change was discussed: https://mailarchive.ietf.org/arch/msg/oauth/aYB1seP7c5ou_z55seu6pdJx6fk/
    And this is the change in the draft: https://tools.ietf.org/rfcdiff?url2=draft-ietf-oauth-v2-21.txt#part-13

  10. Log in to comment