剣 KENSAI

OAuth-beveiligingstestchecklist: high-impact authenticatiebugs vinden

3 april 2026 22 min leestijd authentication-guide

OAuth 2.0-kwetsbaarheden staan consequent bovenaan de lijst voor bug bounty-uitbetalingen met kritieke ernst. Een enkele account-takeover via OAuth-misconfiguratie kan escaleren naar volledige organisatorische compromittering — en deze bugs komen voor in applicaties die door miljarden mensen worden gebruikt.

Kerncijfers
  • $10K-$100K — OAuth ATO-uitbetalingen
  • Kritiek — Impactclassificatie
  • 4,2 miljard+ — Wereldwijd getroffen gebruikers
  • OWASP A07 — ID- & authenticatiefouten

OAuth 2.0-fundamenten voor beveiligingstesten

ℹ️ De OAuth 2.0-stromen

Begrijpen welke stroom een doelwit gebruikt bepaalt welke aanvallen te testen. De Authorization Code-stroom (met PKCE) is het veiligst en meest gebruikelijk. De inmiddels verouderde Implicit-stroom heeft talrijke kwetsbaarheden. Client Credentials (machine-naar-machine) heeft zijn eigen aanvalsoppervlak. Nooit gebruiken in productie: Resource Owner Password Credentials.

Belangrijkste OAuth-componenten

Sectie 1: onderschepping van autorisatiecodes

Test 1.1 — Open redirect in redirect_uri

Als de autorisatieserver redirect_uri met onvoldoende precisie valideert, kan een aanvaller de autorisatiecode omleiden naar hun beheerde server.

# Legitimate request
https://auth.example.com/oauth/authorize?
  client_id=app123&
  redirect_uri=https://app.com/callback&
  response_type=code&state=xyz

# Attack attempts
# 1. Path traversal
redirect_uri=https://app.com/callback/../../../evil.com/steal

# 2. Additional path
redirect_uri=https://app.com.evil.com/callback

# 3. Fragment bypass
redirect_uri=https://app.com/callback%23@evil.com

# 4. Query string bypass
redirect_uri=https://app.com/callback?next=https://evil.com

Test 1.2 — Validatie van de state-parameter

⚠️ Ontbrekende/zwakke state = CSRF op OAuth

De state-parameter voorkomt CSRF-aanvallen tijdens OAuth-stromen. Indien afwezig of voorspelbaar, kan een aanvaller slachtoffers dwingen hun accounts te koppelen aan door de aanvaller beheerde identiteiten — wat account-takeover mogelijk maakt.

# Check if state is:
# 1. Present in authorization request
# 2. Validated on callback
# 3. Cryptographically random (not sequential/predictable)
# 4. Single-use (replayed states should be rejected)

# Test: replay the same state parameter
curl -s "https://app.com/oauth/callback?code=VALID_CODE&state=PREVIOUSLY_USED_STATE"
# Should return: error (state already used)

Test 1.3 — Hergebruik van code

# Authorization codes must be single-use
# Test by replaying the code after first exchange:

# First use (legitimate)
POST /oauth/token
code=AUTH_CODE_123&grant_type=authorization_code&...

# Second use (should fail)
POST /oauth/token
code=AUTH_CODE_123&grant_type=authorization_code&...
# Expected: {"error": "invalid_grant"}

Sectie 2: PKCE-bypasstesten

Test 2.1 — PKCE-handhaving

PKCE voorkomt aanvallen die autorisatiecodes onderscheppen. Als een server die PKCE ondersteunt dit niet handhaaft, zijn publieke clients kwetsbaar.

# Start flow WITH PKCE (legitimate)
code_verifier = generate_random_string(64)
code_challenge = base64url(sha256(code_verifier))

GET /authorize?
  code_challenge=abc123&
  code_challenge_method=S256&...

# Attack: exchange code WITHOUT providing code_verifier
POST /token
grant_type=authorization_code&
code=INTERCEPTED_CODE&
redirect_uri=https://legitimate-app.com/callback
# Missing: code_verifier
# If this succeeds -> PKCE not enforced -> Critical vulnerability

Test 2.2 — Downgrade van code_challenge_method

# Attempt to downgrade from S256 to plain (less secure)
GET /authorize?
  code_challenge=ACTUAL_CODE_VERIFIER&
  code_challenge_method=plain&...
  
# If accepted: server may be using plain instead of S256
# Then exchange with code_verifier = original plain text
POST /token
code_verifier=ORIGINAL_PLAIN_TEXT&...

Sectie 3: tokenkwetsbaarheden

Test 3.1 — Lekkage van access token via Referrer

⚠️ Fragment vs. query tokenafhandeling

In de Implicit-stroom (verouderd maar nog steeds aangetroffen) worden access tokens in URL-fragmenten (#access_token=...) niet meegestuurd in Referer-headers — maar tokens in queryparameters (?access_token=...) wel. Controleer altijd hoe tokens in URL's verschijnen.

Test 3.2 — Escalatie van tokenscope

# Request minimal scope
GET /authorize?scope=read:email&...

# After getting access token, test unauthorized API calls
GET /api/user/delete
Authorization: Bearer ACCESS_TOKEN_WITH_READ_SCOPE
# Should return 403, not 200

Test 3.3 — JWT access token-kwetsbaarheden

# If access tokens are JWTs, test:

# 1. Algorithm confusion (RS256 -> HS256)
header = {"alg": "HS256", "typ": "JWT"}
# Sign with the public key as HMAC secret

# 2. None algorithm
header = {"alg": "none", "typ": "JWT"}
# Some libraries accept unsigned tokens

# 3. Kid injection (if 'kid' is used)
header = {"alg": "HS256", "kid": "' OR 1=1--"}
# SQL injection in kid parameter

# Tools:
# jwt_tool: python3 jwt_tool.py TOKEN -T (tamper)
# jwt-cracker: for weak secrets

Test 3.4 — Bypass van refresh token-rotatie

# Test if old refresh tokens are invalidated after rotation
POST /token
grant_type=refresh_token&refresh_token=OLD_REFRESH_TOKEN

# If this returns a new token -> old token not invalidated
# Attacker who stole old refresh token can still get new access tokens

Sectie 4: account-takeover-scenario's

Test 4.1 — OAuth-accountkoppeling zonder e-mailverificatie

💡 High-impact bevindingspatroon

Als een slachtoffer zich aanmeldt met e-mail/wachtwoord (e-mail: victim@gmail.com) en een aanvaller een Google OAuth-account met hetzelfde e-mailadres kan koppelen om het account van het slachtoffer over te nemen — zonder dat Google eigendom verifieert — is dat een kritieke account-takeover. Dit komt vooral vaak voor op platforms die het koppelen van meerdere authenticatieproviders toestaan.

Test 4.2 — Pre-account-takeover

# Attack scenario:
# 1. Attacker creates account with victim@example.com (before victim registers)
# 2. Attacker links their OAuth provider to this email
# 3. When victim later registers with Google OAuth using victim@example.com
# 4. Victim gains access to attacker's pre-created account (or vice versa)

# Test by:
# 1. Register account with victim's email (before they exist)
# 2. Connect OAuth from different email
# 3. Try to access victim's future account

Test 4.3 — Tokenfixatie

# Some implementations allow specifying access_token in request
# or don't properly bind tokens to sessions

# Test: force a known token value
GET /oauth/callback?access_token=KNOWN_VALUE

# If the app accepts and uses this token -> token fixation

Test 4.4 — Verwarring tussen sub-/e-mailclaims

# If an application ties accounts to email (not sub):
# Register malicious OAuth provider with victim's email as their 'email' claim
# Some providers let you set arbitrary email claims

# Test by creating custom OAuth provider with:
{
  "sub": "attacker-unique-id",
  "email": "victim@example.com",
  "email_verified": true
}

Sectie 5: kwetsbaarheden in de autorisatieserver

Test 5.1 — Zwakte in clientauthenticatie

# Confidential clients must authenticate with client_secret
# Test if client_secret is optional:
POST /token
grant_type=authorization_code&
code=CODE&
client_id=CONFIDENTIAL_CLIENT_ID&
# Missing: client_secret
# If this works -> client authentication not enforced

Test 5.2 — Misbruik van device authorization-stroom

# Device flow (for input-constrained devices) can be abused for phishing
POST /device/code
client_id=LEGITIMATE_CLIENT

# Returns user_code that victim enters at verification_uri
# Attacker uses this for social engineering:
# "Enter code XXXX-XXXX at accounts.target.com/activate"

Complete OAuth-testchecklist

Pre-autorisatiefase
  • ☐ Breng alle OAuth-stromen en eindpunten in kaart
  • ☐ Identificeer client_id, scope-waarden, redirect-URI's
  • ☐ Controleer of PKCE is geïmplementeerd
  • ☐ Noteer welk type access tokens worden uitgegeven (opaak vs. JWT)
Tests voor autorisatieverzoeken
  • ☐ Test redirect_uri-parameter — open redirects, path traversal, host-bypass
  • ☐ Test state-parameter — aanwezig? gevalideerd? willekeurig? eenmalig bruikbaar?
  • ☐ Test scope-manipulatie — vraag ongeautoriseerde scopes aan
  • ☐ Test downgrade van response_type (code → token indien implicit ondersteund)
  • ☐ Test downgrade van PKCE code_challenge_method
Tokenuitwisselingstests
  • ☐ Test hergebruik van autorisatiecode (replay na uitwisseling)
  • ☐ Test codevervaltijd (codes moeten binnen 60-600 seconden vervallen)
  • ☐ Test PKCE-handhaving (uitwisseling zonder code_verifier)
  • ☐ Test clientauthenticatie (laat client_secret weg)
  • ☐ Test cross-client-code-uitwisseling (gebruik code van client A bij client B)
Tokenbeveiligingstests
  • ☐ Indien JWT: test algoritmeverwarring, none-algoritme, kid-injectie
  • ☐ Test tokenscopehandhaving bij de resource server
  • ☐ Test refresh token-rotatie en -intrekking
  • ☐ Controleer op tokenlekkage in logs, Referer-headers
  • ☐ Test tokenbinding (zijn tokens gekoppeld aan sessies/IP's?)
Accountkoppelingstests
  • ☐ Test OAuth-koppeling zonder e-mailverificatie
  • ☐ Test de pre-account-takeover-stroom
  • ☐ Test overeenkomst van e-mailclaim vs. sub-claim voor accounts
  • ☐ Test ontkoppelingsgedrag (kan een aanvaller de provider van het slachtoffer ontkoppelen?)

OAuth-bugvoorbeelden uit de praktijk

BugtypePlatformImpactBounty
redirect_uri-bypassFacebookAccount-takeover$25.000
Ontbrekende state-parameterAirbnbCSRF → ATO$8.000
Hergebruik van codeMicrosoftReplay-aanval$15.000
JWT none-algoritmeAuth0Authenticatie-bypass$10.000
E-mailverwarring ATOSlackAccount-takeover$20.000
PKCE niet gehandhaafdMeerdereCode-onderschepping$5.000+

Testtools

🛡️ Automatiseer OAuth-beveiligingstesten

De AI-aangedreven scanner van KENSAI test uw OAuth-implementaties automatisch op onderschepping van autorisatiecodes, PKCE-bypass, tokenkwetsbaarheden en account-takeover-scenario's.

Start gratis scan →