The importance of the “state” parameter in OAuth

Sebastian Łaskawiec
Keycloak
Published in
3 min readJul 23, 2021

--

The “state” parameter from oAuth spec is one of the most interesting ones. Since it’s optional, it is often underestimated.

The oAuth 2.1 spec defines the state parameter as follows:

An opaque value used by the client to maintain state between the request and callback. The authorization server includes this value when redirecting the user-agent back to the client.

The “state” parameter is sent during the initial Authorization Request and sent back from the Authorization Server to the Client along with the Code (that can be later exchanged to a token). The Client should use the content of this parameter to make sure the Code it received matches the Authorization Request it sent.

As there are not many requirements for the content of the state parameter but both OAuth spec and OAuth 2.0 Threat Model and Security Considerations mention that:

  • It should not contain any sensitive information as they might be stored in the browser’s history and server access logs (as a reminder, the state is transmitted via a query parameters)
  • The state parameter should be seeded with a secure random (this avoids CSRF attacks)

From the Client application development perspective, the “state” parameter is very helpful for restoring a user’s session. This may mean different things for different applications but the most common example is a web-based Client application querying an in-memory data grid for cached objects for a specific user. The state parameter might point to a user session key.

So… what can go wrong if we don’t include it? Well… let’s check it out!

I’ve created a small CLI application (which is just a modified version of the one from my previous blog post). The CLI application redirects the user’s browser to the Keycloak login page and at the same time it starts the HTTP server on localhost to catch the Authorization Response with the Code.

Now, imagine an attacker who starts his login flow but cancels it after obtaining the Code. In other words, an attacker never exchanges the Code for a Token. The Code will expire after a while, but since then, it can be exchanged for a token by anybody who has it.

From the victim’s perspective, when a CLI application opens the login page up, the HTTP server is already started and listening up for the Authorization Responses. For a short period of time, the application is vulnerable to injecting an attacker’s Code right into its endpoint. If that happens, the victim will obtain the attacker’s Access Token.

Using a simple curl command, we can inject such a code manually:

You may think that logging into 3rd party services using the attacker’s identity is not dangerous, but this is not true. Imagine that you’re checking your mobile phone payment information. Who’s bill will you pay in this scenario? That’s right — the attackers!

You may want to ask — is there any overlap with PKCE?

oAuth 2.1 spec standardizes the use of PKCE. It’s main idea is that the Client sends a Code Challenge with the Authorization Request and the Code Verifier with the Token Request. The Authorization Server then validates the integrity of those two. Conceptually, PKCE is similar to the code verified but implemented on the Authorization Server side. When using PKCE, the described attack scenario would not take place, unless the Authorization Server doesn’t strictly enforce it. In that case, an attacker may try to leverage the PKCE Downgrade Attack. Again, using the “state” parameter reduces that risk. Bottom line — if you want to be on the safe side, use the “state” parameter together with PKCE.

The countermeasures for these sorts of attacks are all similar. They use a Client-generated secret value that is being somehow compared with the Authorization Server response. This principle is used by the state parameter, the nonce parameter used by OpenID Connect or PKCE. Apart from protecting against CSRF attacks, the state parameter can also be helpful in reconstructing the application state. These sorts of use cases are extremely useful in all caching scenarios.

The code for the this example might be found here: https://github.com/slaskawi/state-field-validation

--

--

Sebastian Łaskawiec
Keycloak

Sebastian is an enthusiastic Software Engineer who focuses on designing software with security as a first class citizen.