1

Exploitation

Missing Authentication

🔬

Exploitation

The simplest authentication bypass is also one of the easiest to introduce: an endpoint or function that should require authentication simply has no authentication check at all.

A common real-world example is an API where /api/users/list is correctly protected by authentication middleware, but a newer endpoint such as /api/users/export is added later and the developer forgets to apply the same middleware. Another possibility is an internal or administrative route accidentally being registered in the public routes configuration instead of the protected one.

The preferred defense is deny by default: sensitive routes should live inside explicitly protected middleware groups, with public routes being the exception. Every endpoint that exposes sensitive data or performs privileged functionality should have its authentication requirements verified independently.

2

Exploitation

Authentication Logic Flaws

🔬

Exploitation

Authentication can also fail because of bugs in the conditional logic that decides whether authentication succeeds or fails.

A classic PHP example is comparing password hashes with the loose == operator instead of using a strict, purpose-built comparison function. PHP's loose comparison rules can interpret certain strings that look like scientific notation as numbers. For example:

"0e123456" == "0e999999" // true

Both strings can be interpreted as the numeric value 0, even though the actual strings are completely different.

If an application uses this kind of comparison when verifying credentials, a specially constructed value with a matching 0e... pattern could potentially satisfy the authentication check without being the legitimate password hash.

// Vulnerable 
if ($storedHash == $inputHash) { 
    // authenticated }
// Correct
if (hash_equals($storedHash, $inputHash)) {
    // authenticated}

The underlying problem is not a weakness in the hashing algorithm. The application has correctly generated different values but compared them incorrectly.

For security-sensitive values, developers should avoid general-purpose loose equality and use comparison mechanisms appropriate to the data. In PHP, hash_equals() performs a timing-safe comparison of two strings and avoids the type-juggling behavior of ==.

When reviewing authentication code, don't only ask whether passwords are hashed securely. Examine the exact condition that determines whether the supplied credential is accepted.

3

Exploitation

Forced Browsing and Unprotected Routes

🔬

Exploitation

This occurs when an attacker reaches a protected resource through a route that was never actually guarded. The underlying mistake is assuming that users will only reach the resource through the application's normal navigation or login flow.

For example, suppose an application has a login page at /login.php and an administrative page at /admin/dashboard.php. If the dashboard does not independently require a valid authenticated session whether through its own check or properly applied authentication middleware an attacker can simply request the dashboard URL directly.

The developer may have assumed: “You can only get here by going through /login.php first.”

But HTTP clients do not have to follow the application's intended navigation flow. An attacker can request any known or discovered URL directly.

A real security boundary must be enforced server-side on the protected resource or by a middleware layer that is guaranteed to cover it.

4

Exploitation

Parameter Manipulation

🔬

Exploitation

Authentication fails when the application trusts a value controlled by the client instead of deriving the authentication state independently on the server.

A textbook example is a hidden form field:

<input type="hidden" name="role" value="user">

The field may look like application state, but it is still supplied by the browser. If the server trusts it when processing the request instead of determining the user's actual role from trusted server-side state, an attacker can modify it before submitting the form:

role=user  →  role=admin

The same problem can appear in authentication flows. Consider a login process that redirects the browser to:

/dashboard?authenticated=true

If /dashboard treats that URL parameter as proof that authentication succeeded instead of checking a valid server-side session, anyone can simply request the URL directly with authenticated=true.

5

Exploitation

Client-Side Authentication Checks

🔬

Exploitation

The same principle applies when authentication logic exists entirely in JavaScript:

if (localStorage.getItem('isLoggedIn') === 'true') {
    showAdminPanel();
}

An attacker controls the browser, including its JavaScript state. Changing the value:

localStorage.setItem('isLoggedIn', 'true');

may cause the interface to display the protected panel, but that alone is only a UI bypass.

6

Explanation

Authentication State Confusion

Authentication state confusion occurs when a multi-step authentication process loses track of which steps a particular session has actually completed.

This is specifically about the authentication process itself, rather than post-login session management.

Consider a two-step authentication flow:

The user submits a password.
The user submits an OTP.
Only after both steps succeed does the server establish a fully authenticated session.

The critical security requirement is that the server must independently verify that step 1 was successfully completed for this specific authentication attempt before accepting step 2.

If the OTP endpoint instead trusts a client-controlled flag, uses an authentication state that defaults to an unsafe value, or fails to invalidate intermediate state correctly between attempts, an attacker may be able to invoke the OTP step directly from a fresh or otherwise unauthorized session.

The problem is not that the OTP itself is necessarily broken. The problem is that the application has failed to enforce the relationship between the authentication steps.

A secure design treats intermediate authentication state as server-controlled state and binds it to the specific authentication attempt. The server should explicitly verify that the required previous step succeeded before allowing the next step to advance the authentication state.

7

Summary

Key Takeaways

Summary

The failure usually takes one of three forms:

Missing verification — a protected route or function never checks authentication at all.
Incorrect verification — the application performs a check, but flawed comparison or conditional logic allows an invalid credential or state to pass.
Misplaced verification — the check exists, but it happens somewhere the attacker controls, such as a hidden form field, browser-side JavaScript, or a URL parameter.

The same problem also appears when an application relies on an unguarded direct route or assumes that an earlier authentication step must have occurred simply because the normal user flow would have required it.

Never trust the client, and never assume a security-critical step happened just because the normal flow would have required it.