Potential Vulnerability about parameter ‘state’ in OAuth2

Several weeks ago when I was researching how to be an OAuth2.0 provider, I found the following potential vulnerability when a user is requesting an access token from OAuth provider.

Senario:

If user A requests a authentication code with state=1234 and he gets redirected to: http://abc.com/callback?state=1234&code=9IDE3F

If user B requests a authentication code with state=5678 and he gets redirected to: http://abc.com/callback?state=5678&code=3R34TG

Let’s assume A and B exchange the callback url with each other, if the application server has a state detecting mechanism, then neither A nor B can get access token because the application server has rejected the requests. 

That seems fine. But let’s use a different way:

Assume A and B exchange only “code" and keep their own states.

Then A requests: http://abc.com/callback?state=1234&code=3R34TG

At this time, application server can be easily cheated since it only checks the state and the state ‘1234’ does come from A’s original request.

Once the application server validates the request, access token can be returned to A.

In terms of most popular OAuth2.0 providers (i.e. sina weibo, tencent weibo, even including google oauth2, etc), after analyzed their API documents, I’ve found no one checks the integrity of state and code. ‘State’ is always treated as an optional parameter for them. In another words, ‘state’ is only used for application server to check the validation of the request but not for themselves. Parameter ‘state’ is not applicable for requesting an access token. Once application server is defrauded, I don’t think it makes sense for OAuth providers to give the access token to suspicious user. It’s still their obligation to store users’ information more securely.

 

(quoted from Google OAuth2 )

Here is an example of a complete OpenID Connect authentication URI, with line breaks and spaces for readability:

https://accounts.google.com/o/oauth2/auth?
 client_id=424911365001.apps.googleusercontent.com&
 response_type=code&
 scope=openid%20email&
 redirect_uri=https://oa2cb.example.com/&
 state=security_token%3D138r5719ru3e1%26url%3Dhttps://oa2cb.example.com/myHome&
 [email protected]&
 openid.realm=example.com&
 hd=example.com

Users are required to give consent if your app requests any new information about them, or if your app requests account access that they have not previously approved.

3. Confirm anti-forgery state token

The response is sent to the redirect_uri that you specified in the request. All responses are returned in the query string, as shown below:

https://oa2cb.example.com/code?state=security_token%3D138r5719ru3e1%26url%3Dhttps://oa2cb.example.com/myHome&code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7

On the server, you must confirm that the state received from Google matches the session token you created in Step 1. This round-trip verification helps to ensure that the user, not a malicious script, is making the request.

4. Exchange code for access token and ID token

The response includes a code parameter, a one-time authorization code that your server can exchange for an access token and ID token. Your server makes this exchange by sending an HTTPS POST request. The POST request is sent to the token endpoint, which you should retrieve from theDiscovery document using the key token_endpoint. The following discussion assumes the endpoint ishttps://accounts.google.com/o/oauth2/token. The request must include the following parameters in the POST body:

Field Description
code The authorization code that is returned from the initial request.
client_id The client ID that you obtain from the Developers Console, as described in Obtain OAuth 2.0 credentials.
client_secret The client secret that you obtain from the Developers Console, as described in Obtain OAuth 2.0 credentials.
redirect_uri The URI that you specify in the Developers Console, as described in Set a redirect URI.
grant_type This field must contain a value of authorization_code, as defined in the OAuth 2.0 specification.

The actual request might look like the following example:

POST /o/oauth2/token HTTP/1.1
Host: accounts.google.com
Content-Type: application/x-www-form-urlencoded

code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=8819981768.apps.googleusercontent.com&
client_secret={client_secret}&
redirect_uri=https://oauth2-login-demo.appspot.com/code&
grant_type=authorization_code

(quoted from stackoverflow)

When your application exchanges the authorization code for an access token, you want to be sure that the OAuth flow which resulted in the authorization code provided was actually initiated by the legitimate user. So, before the client application kicks off the OAuth flow by redirecting the user to the provider, the client application creates a random state value and typically store it in a server-side session. Then, as the user completes the OAuth flow, you check to make sure state value matches the value stored in the user’s server-side session– as that indicates the user had initiated the OAuth flow.

A state value should typically be a pseudo-random unguessable value. A simple value can be generated as an int with the rand() function in PHP, though you could get more complex as well to provide greater assurance.

The state exists to prevent things like me sending you a link via e-mail which contains an authorization code for my account, you clicking on it and the application pushing all the data into my account unbeknownst to you.

Some additional information is in the OAuth 2.0 threat model document: http://tools.ietf.org/html/draft-ietf-oauth-v2-threatmodel-00

In particular, see the section on CSRF protection: http://tools.ietf.org/html/draft-ietf-oauth-v2-26#section-10.12

發表迴響

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料