10

I'm trying to get my head around how PKCE works in a mobile app and there's something I don't quite understand.

So from what I can gather the client app creates a random cryptographically secure string known as the code-verifier. This is then stored. From this, the app then generates a code challenge. The code challenge is then sent in an API request to a server along with how the challenge was generated e.g S256 or plain. The server stores this challenge along with an appropriate authorization_code for the request in question.

When the client then tries to exchange the code for an access token it also sends the original code-verifier in the request. The server then retrieves the stored challenge and the method originaly used to generate it for this particular code and generates the equivalent s256/plain hash and compares them. If they match, it returns an access token.

What I don't get is how this is supposed to replace a secret in a client app? Surely if you wanted to spoof this you would just take the client_id as normal and generate your own code-verifier and challenge and you're in the same position as if PKCE wasn't required in the first place. What is PKCE actually trying to solve here if the original idea was that it is basically a 'dynamic secret'? My assumption is it's only there if someone happens to be 'listening' into when the auth_code is returned, but if you're using SSL again is this needed? It’s billed as replacing the fact you shouldn’t store a secret in a public app but the fact the client is responsible for generating rather than a server feels like it’s not actually helping there.

7

This write-up Okta has on this subject explains this pretty well IMHO.

I believe it's because PKCE is intended for native applications (e.g. Android, iOS, UWP, Electron, etc.) where you leave the security context of your application and go to the browser to authenticate, and rely on the secure return to your application from the browser. You don't necessarily have TLS on the redirect back to your application (in the case of custom schemes, you are relying on the OS to bring the response back to your application) so in the event your authorization code goes somewhere malicious, the receiving app wouldn't be able to get an access token without the dynamic secret.

The merits of a dynamic secret on a public client are obvious here - and the assumption for PKCE is that it is not difficult to intercept the response from the browser to your application.

  • What's the point of hashing the code verifier? – Eric Eskildsen Nov 28 '18 at 19:31
  • 1
    If I had to guess I'd say because you need to pass the code to the authorization endpoint securely, and since you're directing the user to a new application context, you cannot be sure that the code is securely transferred from your application to the browser. So you hash it, and after receiving the authorization code, your application can securely send both codes plainly over TLS directly to the authorization endpoint and the server can compute the hash and compare it against what was initially sent. I hope that makes sense! Read more from the rfc – someone1 Nov 29 '18 at 14:39
  • Hashing the code verifier makes sense. Why does the spec support the "plain" code challenge mode though, where code challenge == code verifier. Wouldn't the attacker just intercept the code challenge in this case, defeating its purpose? – Dmitry Pashkevich Jan 21 at 15:23
  • From the RFC: If the client is capable of using "S256", it MUST use "S256", as "S256" is Mandatory To Implement (MTI) on the server. Clients are permitted to use "plain" only if they cannot support "S256" for some technical reason and know via out-of-band configuration that the server supports "plain". The plain transformation is for compatibility with existing deployments and for constrained environments that can't use the S256 transformation. – someone1 Jan 22 at 0:23
6

The reason PKCE is important is that on mobile OS, the OS allows apps to register to handle redirect URIs so a malicious app can register and receive redirects with the authorization code for legitimate apps. This is known as an Authorization Code Interception Attack.

Authorization Code Interception Attack

This is described by WSO2 here:

Since multiple applications can be registered as a handler for the specific redirect URI, the vulnerability of this flow, is that a malicious client could also register itself as a handler for the same URI scheme that a legitimate application handles. If this happens, it is a possibility that the operating system will parse the URI to the malicious client. The flow of this attack is illustrated in the following diagram.

In some operating systems such as Android, in step 5 of the flow, the user is prompted to select the application to handle the redirect URI before it is parsed using a "Complete Action Using" activity. This may avoid a malicious application from handling it, as the user can identify and select the legitimate application. However, some operating systems (such as iOS) do not have any such scheme.

To understand this better, here is a diagram and discussion from OpenID. You can see that the mobile System Browser has responsibility to receive the redirect URI and route it to the correct app.

Native app Redirect URI routing

However, because mobile OSes can allow many apps to register for the same redirect URI, a malicious app can register for and receive a legitimate authorization code as shown in this diagram, also by WSO2:

PKCE Malicious App

Attack Mitigation by PKCE

PKCE mitigates this by requiring shared knowledge between the app initiating the OAuth 2.0 request (request auth code) and the one exchanging the auth code for token. In the case of an Auth Code Interception Attack, the malicious app does not have the verifier to complete the token exchange.

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.