Does Proof Key for Code Exchange (PKCE) work without TLS? - oauth-2.0

Below I have tried to justify my question, "Does Proof Key for Code Exchange (PKCE) work without TLS? I believe I am missing understanding part of the spec and was hoping for some direction. I also have a second question, Does PKCE only relate to using Cookies to store the auth token (not mentioned in spec).Please help me identify the misinformation or lack of information in my comments below.
rfc7636's introduction section describes an attack authorization code interception attack on public clients. It states,
the attacker intercepts the authorization code returned from the
authorization endpoint within a communication path not protected by
Transport Layer Security (TLS) ...
The Introductions PreCondition section item 4 indicates that TLS not protecting the response .
The Introductions "To mitigate this attack" paragraph the states
This works as the mitigation since the attacker would not know this
one-time key, since it is sent over TLS and cannot be intercepted.
and
this extension utilizes a dynamically created cryptographically random
key called "code verifier".
RFC6949 mentions attacks around the use of cookies; however, rfc7636 does not specify pertaining only to cookies or local storage of the Auth token. Therefore is seems it would resolve an attack on the request if the auth token was also stored dynamically. Is this the case?

PKCE is used as a proof that the party which initiated authentication (via a browser redirect) is the same party completing it (via an HTTP POST).
It works without TLS, as in steps 4 and 8 of my blog post, where the code verifier in step 8 must match the code challenge in step 4.
Cookies are not directly related to PKCE. However, in a browser based app without PKCE or a client secret, malicious browser code only needs to send the authorization code to get tokens. If tokens are stored in cookies then the malicious code can only perform session hijacking.
PKCE was originally introduced for public clients that cannot use a client secret. These days, it is recommended for all clients that use the code flow, including those with a client secret. And of course TLS should also be used.

Related

How CSRF attack can occur with missing State parameter and existence of PKCE is OAuth 2.0 flow

Imagine a situation that the client doesn't provide state parameter but is using PKCE method.
How CSRF attack can be performed?
Is there any need for the state parameter if the client is using PKCE?
I read https://security.stackexchange.com/a/215027 but it didn't provide a good example.
The state parameter, is something that the client validates and unfortunately, not every client implementation validates the state parameter properly. So it is a risk letting the clients be responsible for this.
PKCE is validated by the authorization server and probably they do a better job of verifying this than letting the clients do it.
So I think they both compliment each other, so if one of the parties involved forgets to validate, then the other protection will still cover it.
They serve a bit different purpose. PKCE is a mechanism which allows the Authorization Server to make sure that the client which exchanges authorization code for a token is the same client that initiated the flow.
The state parameter is only used by the client. Authorization server doesn't care for it. It is used by the client to verify that the token response it gets from the Authorization Server was indeed initialized by this client earlier.
So, if the client uses PKCE but doesn't use state, it protects itself only from some vectors of attacks. It's best that these features are used together.

PKCE: Surely hacker can still steal access token?

From my understanding, the advantage that Authorization Code Flow has over Implicit Flow is that with ACF, the access token gets sent to a server side app rather than to a browser app. This makes the access token much harder to steal, because the access token never reaches the browser (and is thus not susceptible to a Cross Site Scripting attack).
I would have thought that PKCE would try to solve this issue. But it does not. The access token is still sent to the browser. Hence it can still be stolen.
Is there something I am missing here?
Many thanks.
Authorization Code Flow (PKCE) is considered superior security to the previous solution of Implicit Flow:
With implicit flow the access token was returned directly in a browser URL and could perhaps be viewed in logs or the browser history
With Authorization Code Flow this is handled better, with reduced scope for exploits:
Phase 1: A browser redirect that returns a one time use 'authorization code'
Phase 2: Swapping the code for tokens is then done via a direct Ajax request
PKCE also provides protection against a malicious party intercepting the authorization code from the browser response and being able to swap it for tokens.
Both are client side flows and their reason for existing is to use access tokens in public clients. Authorization Code Flow (PKCE) is the standard flow for all of these:
Single Page Apps
Mobile Apps
Desktop Apps
In the SPA case the token should not be easily stealable, especially if stored only in memory as recommended. However, there are more concerns when using tokens in a browser, since it is a dangerous place, and you need to follow SPA Best Practices.
In the browser case there are other options of course, such as routing requests via a web back end or reverse proxy in order to keep tokens out of the browser, and dealing with auth cookies in addition to using tokens.
I think your are right. The tokens are not in a http-only cookie, and are therefore accessible by a malicious script (injected via an XSS attack). The attacking script can read all tokens (after a successful and normal auth flow) from local storage (or wherever they got put) and use them.
I think CORS protections should prevent the malicious script from sending the tokens out to an attacker directly, which would be a devastating failure, as this potentially includes a long lived refresh token. Therefore, I suspect configuring CORS correctly is super critical when using these local-client based flows (by local-client I mean a browser, mobile app, or native PC app).
In short, these local-client flows can be made secure, but if there is an XSS attack, or badly configured CORS, then those attacks can become extremely dangerous - because the refresh token could potentially be sent to the attacker for them to use at will in their own good time, which is about as bad as an attack can get.

In OpenID Connect with PKCE, how does the client know which code_verifier to send with which authorization code after user redirect?

I'm taking a ASP.NET security course on OpenID Connect authorization code workflow with PKCE protection against code replay attack. There is one aspect of this process that I don't understand.
The process:
Relying client generates PKCE code_verifier, hash it into code_challenge, and send the user to the authorization server with the code_challenge as a query parameter.
The authorization server stores the code_challenge, issues authorization code, and redirects user back to client with the authorization code.
The client sends the authorization code along with the original code_verifier to exchange for tokens. The authorization server verifies that the code_verifier indeed hashes into code_challenge before issuing the token.
My question is step #3: since HTTP is stateless, how does the client know which code_verifier to send along with the authorization code? Is this code_verifier stored in a cookie on the user agent?
Using a cookie to store the "code verifier" appears to violate the spirit of the OpenId specification and just appears to be a big security hole.
In brief, the spec expects you to use S256 to hash the code verifier and the code verifier to be kept secret from eavesdropping or being guessed.
PKCE RFC Section 7.1 says the "The security model relies on the fact that the code verifier is not learned or guessed by the attacker. It is vitally important to adhere to this principle. As such, the code verifier has to be created in such a manner that it is cryptographically random and has high entropy that it is not practical for the attacker to guess."
... "The use of "S256" protects against disclosure of the "code_verifier" value to an attacker."
Now it does give an opening to using a cookie if you encrypt the "code verifier". Here is the what the spec says in
Section 7.2: "If the code challenge method is "plain" and the code challenge is to be returned inside authorization "code" to achieve a stateless server, it MUST be encrypted in such a manner that only the server can decrypt and extract it."
Well, OpenId specification has a solution and Microsoft has provided an implementation to follow. Refer to the "state" parameter in the OpenId specification when calling the "authorization" endpoint. The Identity Providers will return the value back to the caller.
The way it is done in .NET Core is using the "state" query string parameter whereby the value is a dictionary but protected (encrypted). For help look through the .NET core code and look at the method "BuildChallengeUrl" in "Microsoft.AspNetCore.Authentication.OAuth.OAuthHandler"
Also, this Site has a great idea that works. It's a valid option.
PKCE was originally intended for mobile clients, which typically make only a single authorization request. But it is now recommended for all clients using the authorization code flow.
You're correct that if the client is a web application serving many users, the client application needs to associate the correct code_verifier with the authorization request.
As you say, the client could store the code_verifier in a cookie, so it will be sent to the client together with the authorization code.
Or it could store it server side on the client and stick the key in the state parameter to the authorization request. The state will be part of the call to the redirect_uri if passed along with the authorization request.

Why retrieving Access Token is a separate step with extra HTTP request in OAuth2?

While learning how OAuth2 works, I cannot figure out why there is a separate step to retrieve Access Token?
A separate step means:
an extract HTTP request
passing Client Secret in the URL
I'd expect the Access Token to be generated in the "authorization" step, encrypted with using the Client Secret, and returned back when redirecting to the Callback URL. Then the client application would decrypt it and use it straight await without issuing an extra HTTP request.
I guess there are some reasons behind having an extra step, and I'm just not aware of them. I hope you can explain the reasons in your answer.
I'm assuming you're talking about the Authorization Code flow and not the Implicit flow, which does return a token directly.
The Authorization Code flow is designed to work with potentially unencrypted servers via a callback URL (this was designed years before Let's Encrypt and the relatively recent encrypt-everything push). Thus, the URL could be intercepted by any intermediate routers/proxies, and sending an access code as part of the callback URL in that environment is a Bad Idea.
So instead, the authorization code is sent. Then the client exchanges the authorization code along with its client secret for an access token. The authorization code can be intercepted, but in the Authorization Code flow, the client secret is actually secret and not known outside your server, so any intercepted authorization code is useless on its own.
These days, encryption is common and free, and unencrypted flows are strongly discouraged. The extra call remains part of the Authorization Code flow for historical reasons.

Do OAuth clients need to validate tokens as part of authorization code grant?

Does an app using authorization code (with or without PKCE) to obtain access + id tokens on behalf of a user need to also validate those tokens (signature, not expired, audience, etc.)?
If so, what for? Since the client is using TLS and pointing to the provider it's been configured with, what attacks/threats does that client also validating the token mitigate?
At first glance you are right and the spec basically allows for not checking tokens directly returned from the token endpoint over TLS as you suggest indeed, but:
Firstly, one may argue that if a signature is present, it is there for a reason and it should be validated since the Provider is also free to return tokens without a signature (alg="none") if it did not want/need the Client to validate.
Secondly, there are known attacks ("IDP mixup") that trick the Client into talking to the wrong token endpoint as a way of stealing the Authorization Code: verifying that the ID token returned does not match the expected signature would at least stop the Client from processing an ID token produced by an attacker.
Thirdly, it would be good for the Client to protect itself against broken or compromised IDPs in general, avoiding replay attacks or similar.
I guess when you're doing all of it in a single domain, i.e. Client, AS and RS are all under control of the same organisation and the relationships between them are fixed and one-to-one only, technically verification would be overkill based on current knowledge and known attacks.
But in case your use case spans multiple security domain, in general it is better to verify than it is to assume.

Resources