| name | oauth-security-anti-pattern |
| description | Security anti-pattern for OAuth implementation vulnerabilities (CWE-352, CWE-287). Use when generating or reviewing OAuth/OIDC authentication flows, state parameter handling, or token exchange. Detects missing CSRF protection and insecure redirect handling. |
OAuth Security Anti-Pattern
Severity: High
Summary
OAuth 2.0/OIDC flows are complex and easily misconfigured. The critical mistake: failing to implement and validate the state parameter. This parameter defends against CSRF attacks during OAuth flows. Missing or predictable state allows attackers to trick victims into logging into the attacker's account, enabling account takeover.
The Anti-Pattern
The anti-pattern is initiating OAuth flows without state parameters, or using predictable values not validated on callback.
BAD Code Example
from flask import request, redirect
OAUTH_PROVIDER_URL = "https://provider.com/auth"
CLIENT_ID = "my-client-id"
CALLBACK_URL = "https://myapp.com/callback"
@app.route("/login/provider")
def oauth_login():
auth_url = (f"{OAUTH_PROVIDER_URL}?client_id={CLIENT_ID}"
f"&redirect_uri={CALLBACK_URL}&response_type=code")
return redirect(auth_url)
@app.route("/callback")
def oauth_callback():
auth_code = request.args.get("code")
access_token = exchange_code_for_token(auth_code)
log_user_in(access_token)
return "Logged in successfully!"
Attack:
- Attacker initiates OAuth flow with own account
- Provider redirects to
https://myapp.com/callback?code=ATTACKER_CODE
- Attacker intercepts and pauses request
- Attacker tricks victim into visiting malicious callback URL
myapp.com associates victim's session with attacker's account
GOOD Code Example
from flask import request, redirect, session
import secrets
@app.route("/login/provider/secure")
def oauth_login_secure():
state = secrets.token_urlsafe(32)
session['oauth_state'] = state
auth_url = (f"{OAUTH_PROVIDER_URL}?client_id={CLIENT_ID}"
f"&redirect_uri={CALLBACK_URL}&response_type=code"
f"&state={state}")
return redirect(auth_url)
@app.route("/callback/secure")
def oauth_callback_secure():
received_state = request.args.get("state")
auth_code = request.args.get("code")
stored_state = session.pop('oauth_state', None)
if stored_state is None or not secrets.compare_digest(stored_state, received_state):
return "Invalid state parameter. CSRF attack detected.", 403
access_token = exchange_code_for_token(auth_code)
log_user_in(access_token)
Detection
- Trace the OAuth flow: Start at the point where your application redirects to the OAuth provider.
- Is a
state parameter being generated?
- Is it cryptographically random and unpredictable?
- Examine the callback endpoint:
- Does it retrieve the
state from the incoming request?
- Does it compare it to a value stored in the user's session before the redirect?
- Is the comparison done in constant time (
hmac.compare_digest) to prevent timing attacks?
- Is the state value single-use (i.e., deleted from the session after being checked)?
Prevention
Related Security Patterns & Anti-Patterns
References