OAuth 2.0 is surprisingly fragile and often implemented with subtle, exploitable flaws that don’t break the core functionality but expose user data.
Let’s see how a typical OAuth flow looks from Burp Suite’s perspective. Imagine a web application that allows users to log in with their Google account.
First, the application redirects the user to Google’s authorization server with a request like this:
GET https://accounts.google.com/o/oauth2/v2/auth?
response_type=code&
client_id=YOUR_CLIENT_ID&
redirect_uri=https://your-app.com/callback&
scope=openid%20email&
state=RANDOM_STRING&
access_type=offline
The user logs into Google, approves the requested scopes, and Google redirects them back to https://your-app.com/callback with an authorization code and the original state:
GET https://your-app.com/callback?
code=AUTHORIZATION_CODE&
state=RANDOM_STRING
Your application then exchanges this code for an access_token by making a POST request to Google’s token endpoint:
POST https://oauth2.googleapis.com/token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded
code=AUTHORIZATION_CODE&
client_id=YOUR_CLIENT_ID&
client_secret=YOUR_CLIENT_SECRET&
redirect_uri=https://your-app.com/callback&
grant_type=authorization_code
Google responds with an access_token, refresh_token, expires_in, and id_token. Your application uses this access_token to make API calls on behalf of the user.
The core problem OAuth 2.0 tries to solve is enabling third-party applications to access resources on behalf of a user without getting the user’s password. It achieves this through a series of redirects and token exchanges. The client_id identifies your application to the authorization server, redirect_uri is where the user is sent back after authorization, scope defines the permissions the application is requesting, and state is a crucial parameter for preventing CSRF attacks.
When testing for vulnerabilities, we’re looking for ways to manipulate this flow. Burp Suite acts as a proxy, intercepting all these requests and responses, allowing us to modify them on the fly or replay them with altered parameters.
One of the most common vulnerabilities is an Open Redirect on the redirect_uri. If the application doesn’t strictly validate the redirect_uri parameter in its initial authorization request, an attacker could trick a user into clicking a malicious link that redirects them to an attacker-controlled site after they’ve authorized the legitimate application.
Diagnosis: In Burp’s HTTP history, observe the initial authorization request sent by your application to the authorization server. Look for the redirect_uri parameter. Manually change it in Burp’s Repeater to a malicious domain (e.g., https://evil.com/redirect). Then, initiate the OAuth flow and see if the user is redirected to evil.com after authorizing your app.
Fix: Ensure the redirect_uri parameter is strictly validated against a pre-registered list of allowed URIs for that client ID. The authorization server should reject any redirect_uri that doesn’t exactly match one of the registered URIs.
Another critical vulnerability is the Missing or Weak state Parameter. The state parameter is supposed to be a unique, unpredictable value generated by the application for each authorization request. It’s sent back by the authorization server, and the application must verify it matches the original. If it’s missing, predictable, or not verified, an attacker can perform a CSRF attack. They could force a user to authorize an application and then, using the code returned, impersonate the user.
Diagnosis: Intercept the initial authorization request and remove the state parameter entirely. Complete the OAuth flow. If your application successfully proceeds and logs you in or grants access without an error, the state parameter is not being used or validated. Alternatively, if you can predict the state parameter (e.g., it’s always 123), you can craft a malicious request.
Fix: Generate a cryptographically secure, random string for the state parameter for each authorization request. Store this state value in the user’s session on your server. When the callback occurs, compare the state parameter from the callback with the state stored in the session. If they don’t match, reject the request.
A more subtle issue is the Authorization Code Leakage. If the authorization code is exposed in the URL fragment (#) instead of the query string (?), or if the redirect_uri is not properly secured (e.g., HTTP instead of HTTPS), the code could be intercepted by an attacker. The authorization code is a one-time-use token that can be exchanged for an access token.
Diagnosis: Examine the callback URL after authorization. If the code appears after a # symbol, it’s not sent to the server via HTTP POST, which is a security risk. If the redirect_uri uses HTTP, any intermediary can read the code.
Fix: Ensure the redirect_uri is always an HTTPS URL. The authorization server should be configured to only issue codes to registered HTTPS redirect_uris. The application should also be configured to use the response_type=code parameter correctly, which typically results in the code being in the query string, not the fragment.
We also need to watch out for Client Secret Leakage. The client_secret is a password for your application. If it’s exposed, an attacker can use it, along with a stolen authorization code or even by initiating their own authorization flow, to obtain access tokens for your application.
Diagnosis: In Burp, inspect the POST request made to the token endpoint. If the client_secret is being sent in the URL, in a cookie, or in plain text in the request body without proper encryption or protection, it’s vulnerable. Some applications might even hardcode it in client-side JavaScript, which is disastrous.
Fix: Always transmit the client_secret securely, ideally using HTTP basic authentication headers or in the request body of a POST request over HTTPS. Never expose it in client-side code or in URLs.
Finally, consider Improper Scope Validation. If an application requests broad scopes (e.g., read write) but only uses a subset of those permissions (e.g., only read), an attacker might exploit this by tricking the user into authorizing the broader scope, and then the attacker’s compromised application could potentially perform actions it wasn’t intended to.
Diagnosis: When initiating the OAuth flow, try changing the scope parameter in Burp to request more permissions than the application normally asks for. If the authorization server allows this and the application can then use those extra, unrequested permissions, there’s a vulnerability.
Fix: The application should only request the minimum scopes necessary for its functionality. The authorization server should ideally enforce that requested scopes are within the allowed set for that client.
After fixing these, the next common issue you’ll encounter is the Invalid Grant error if you try to reuse an authorization code.