Auth bypass from source review
TL;DR: Most auth bypasses readable from source come from one of seven shapes: type juggling, signature/MAC weakness, missing middleware on a specific route, role enum gap, password-reset token reuse, OAuth state/PKCE absence, and parser differential. Audit each route for which shape it’s most susceptible to.
The seven shapes
1. Type juggling / loose comparison
- PHP
==, Ruby==on mixed types, JS==with implicit coerce. - Sinks: token comparison, password compare, ID comparison.
- Look for:
if ($token == $expected)—0e123style hash collisions,0 == "abc"on old PHP. - Fix: strict equality + constant-time compare.
2. Signature/MAC weakness
- JWT
noneallowed (noalgorithmswhitelist on verify). - JWT key confusion: HS256 signed with RSA public key.
- ViewState without MAC.
- Cookie signed with weak/leaked
secret_key_base/APP_KEY/machineKey. - Look for:
jwt.verify(t, key)without 3rd arg;secretchecked into git. - Fix: explicit algorithm list, key rotation, secret manager.
3. Missing middleware on a specific route
- Framework-level auth attached per-route or per-class. One route missed = full bypass.
- Look for: route table, count routes vs auth annotations.
- Spring: count
@PreAuthorizevs@RequestMappingper controller class. - NestJS: count
@UseGuardsvs@Get/@Post. - Rails: every controller should inherit
ApplicationControllerwithbefore_action :authenticate_user!. - Express: middleware order in
app.use+ route registration order. - Fix: default-deny at framework level; routes opt-out, not opt-in.
4. Role enum gap
- Role check uses ordinal/integer, gap allows attacker role to slip into a privileged range.
if (user.role > Role.USER) { adminOnly() }— butRole.GUEST = 0, USER = 1, MOD = 2, ADMIN = 3. Attacker registers with role 2.- Look for: role storage in DB without enum constraint; registration form that accepts
rolefield. - Fix: explicit equality checks; role can only be set by privileged code path.
5. Password-reset token reuse / weak token
- Token not invalidated after use.
- Token derived from time + user id (predictable).
- Token issued without rate limit (brute-forceable).
- Reset link emailed to attacker-controlled address via host-header injection (host-header-injection).
- Look for:
password_reset_tokenstable schema; theconsume/invalidatestep afterverify. - Fix: single-use, expire on use, 128-bit random, bind to user’s current password hash.
6. OAuth state/PKCE absence
stateparameter not generated, not stored in session, not validated on callback → CSRF on auth flow → account takeover.- PKCE absent for public clients (mobile, SPA) → authorization code injection (oauth-authorization-code-injection).
redirect_urinot strictly validated (substring match, regex) → token leak.- Look for:
stategeneration point, session-side storage, callback verify. - See pkce-downgrade-and-bypass.
7. Parser differential
- Two parsers see the same input differently — XML SAML signed by one parser, role read by another (parser-differential-saml-ruby).
- HTTP request smuggling between frontend and backend (http-request-smuggling).
- JSON: comments allowed by one parser, not another, with trailing
}confusion. - Look for: any boundary where data is parsed twice (e.g., signature verifier vs business-logic deserializer).
- Fix: re-canonicalise input before each use; single source of truth for parsed form.
Audit workflow
- Route ↔ auth map. Make a table: each route, what middleware/guard/annotation enforces auth, what enforces authorization (resource ownership), what enforces role.
- Look for asymmetry. A route with auth but no resource check is IDOR; a route with role check but no resource check is BFLA (bfla).
- Diff the seven shapes. For each route, ask “could shape N apply here?” — not all shapes apply everywhere, but the ones that do are quick reads.
- Trace the session. Where is the session created? Where is it validated? Where is it invalidated? Mismatches between these three are auth bugs.
- Test the bypass on the live instance. A source-only finding is a hypothesis; confirmed by curl is a bug.
Common false-positive traps
- Reverse-proxy / load-balancer level auth not visible in source (nginx auth_request, oauth2-proxy sidecar). Find deployment config; otherwise you’ll report bypasses that are blocked at L7.
- Service mesh mTLS — internal endpoints assume mTLS not visible in app code.
- Feature flag gates — code path appears reachable but flag is off.