1

Introduction

Introduction

i

Introduction

Once a user successfully authenticates, most applications issue a session identifier or authentication token that represents:

"This request belongs to a user who has already been authenticated."

On every subsequent request, that credential is presented back to the application instead of requiring the user to submit their password again.

User logs in
      ↓
Credentials verified
      ↓
Authenticated session created
      ↓
Session ID / token issued
      ↓
Future requests present that credential
      ↓
Server recognizes the authenticated user

That makes the session credential a highly valuable target. If an attacker obtains a valid session identifier, the application may treat the attacker's requests as if they came from the victim:

Victim's valid session ID
          ↓
Attacker obtains it
          ↓
Attacker sends it to the application
          ↓
Server recognizes the victim's session
          ↓
Attacker acts as the victim

The attacker may never need to know the victim's password or bypass the original login process at all.

2

Vulnerability

Predictable Session IDs

⚠️

Vulnerability

A session identifier must be unpredictable. If an attacker can guess or compute valid session IDs, they don't need to steal a victim's session at all they can generate candidate identifiers and attempt to use them until they find an active session.

For example:

Sequential:
session=10001
session=10002
session=10003

An attacker who receives:

session=10001

can reasonably predict:

session=10002
session=10003
session=10004
...

The same problem exists when session IDs are derived from predictable values:

// Weak — based on predictable inputs
$session = md5($username . time());

Or when something merely looks random because it has been encoded:

Base64(timestamp)
→ encoded, but not random

1724493600
→ MTcyNDQ5MzYwMA==

Encoding or hashing predictable data does not create cryptographic randomness. If an attacker can reconstruct or narrow down the original inputs, they may be able to generate the same or nearby session values.

The attack becomes:

Observe predictable pattern
        ↓
Generate candidate session IDs
        ↓
Submit candidates
        ↓
One matches an active session
        ↓
Attacker inherits that session's identity

The defense is to generate session identifiers using a cryptographically secure random number generator, rather than constructing them from usernames, timestamps, counters, IP addresses, or custom algorithms.

Conceptually:

Bad
→ predictable input + custom transformation

Good
→ cryptographically secure random value

For example:

// Cryptographically secure random bytes
$sessionId = bin2hex(random_bytes(32));

A secure session identifier should have sufficient entropy to make guessing computationally infeasible — 128 bits of entropy or more is a reasonable baseline — and should reveal no meaningful relationship to:

  • The user's identity

  • Their username or account ID

  • The time they logged in

  • Their IP address

  • Other active sessions

3

Vulnerability

Weak Session Id Randomness

⚠️

Vulnerability

This vulnerability is closely related to predictable session IDs, but the weakness lies specifically in the source of randomness rather than the visible structure of the identifier.

A session ID can look completely random:

aK9xP4mL7vQ2nR8z...

and still be predictable if it was generated using a non-cryptographic random number generator.

For example:

// Weak — general-purpose PRNG
$sessionId = md5(rand() . time());

The output may look like a random hash, but hashing weak or predictable values does not make the underlying randomness secure.

Common causes include:

rand() / similar general-purpose PRNG
→ designed for statistical randomness, not security

Poorly seeded PRNG
→ predictable internal state

Timestamp-based seed
→ attacker can narrow the possible values

Custom random-generation logic
→ security depends on implementation mistakes

For example:

Seed = current Unix timestamp

Server creates session at approximately:
14:32:17

Attacker knows the approximate time
        ↓
Tests possible seeds around that moment
        ↓
Attempts to reproduce generated values

The problem is that random-looking is not the same as cryptographically unpredictable.

Non-cryptographic PRNGs are generally designed for speed or statistical distribution rather than resisting an attacker attempting to predict their output. Depending on the generator and how it is used, knowledge of the seed, internal state, or sufficient output may allow future values to be predicted.

The defense is to use a cryptographically secure random number generator (CSPRNG) provided by the language or framework:

PHP
→ random_bytes()

Node.js
→ crypto.randomBytes()

Java
→ SecureRandom

Python
→ secrets

Applications should avoid manually reinventing session generation when the framework already provides secure session handling.

A secure session ID needs more than a random-looking format. Its underlying randomness must come from a cryptographically secure source, so an attacker cannot feasibly predict the next valid identifier.

4

Vulnerability

Session Fixation

⚠️

Vulnerability

Session fixation occurs when an attacker can cause a victim to use a session identifier the attacker already knows before the victim authenticates.

The attack works like this:

1. Attacker obtains or creates a valid session ID

session=ATTACKER_KNOWN_ID
        ↓
2. Victim is induced to use that session
        ↓
3. Victim logs in
        ↓
4. Application keeps the same session ID
   and marks it as authenticated
        ↓
5. Attacker uses the already-known ID
        ↓
Attacker accesses the victim's authenticated session

For example, the attacker may first obtain an unauthenticated session from the application:

Attacker visits application

Server:
session=abc123
authenticated=false

The attacker then finds a way to cause the victim's browser to use:

session=abc123

The critical moment happens when the victim logs in.

A vulnerable application effectively does this:

Before login:

session=abc123
authenticated=false
        ↓
Victim enters valid credentials
        ↓
After login:

session=abc123
authenticated=true

The session identifier did not change. The application simply upgraded an existing session into an authenticated one.

Since the attacker already knows abc123, they can continue using it:

Victim:
session=abc123
→ authenticated

Attacker:
session=abc123
→ same authenticated session

The vulnerability therefore depends on one question:

Does authentication create a new session ID?

Yes
→ attacker-known pre-login ID becomes useless
No
→ existing attacker-known ID may become authenticated

The defense is to regenerate the session identifier immediately after successful authentication:

Before login:

session=abc123
        ↓
credentials verified
        ↓
old session discarded
        ↓
new session generated
        ↓
session=NEW_RANDOM_ID
        ↓
authenticated=true

The attacker may still know the old identifier:

Old session:
abc123

New authenticated session:
x7kP...random...

But the old value no longer represents the victim's authenticated session.

Session regeneration is also important when the security context changes, such as after:

  • Logging in

  • Switching accounts

  • Re-authenticating for a sensitive action

  • Elevating privileges

Never upgrade an existing, potentially attacker-controlled session into an authenticated or higher-privilege session. Replace the identifier whenever the user's security context changes.

5

Vulnerability

Session ID Reuse

⚠️

Vulnerability

A session should stop working when the security event that justified its existence is no longer valid.

Session reuse failures generally occur when a session credential remains valid after the application should have invalidated it, or when the same authenticated session continues to be accepted in contexts the application's security model did not intend.

Common invalidation events include:

User logs out
→ session should be invalidated

Password is changed
→ existing sessions may need to be invalidated

Account is confirmed compromised
→ active sessions should be revoked

Account is disabled
→ existing access should stop

A common failure looks like this:

User clicks "Log out"
        ↓
Browser deletes or stops sending cookie
        ↓
User sees login page
        ↓
But server-side session still exists

From the user's perspective, logout appears successful.

But if an attacker previously obtained the session credential:

Attacker has:
session=abc123

they can continue using it:

GET /account
Cookie: session=abc123
        ↓
Server finds session
        ↓
authenticated=true
        ↓
Access granted

The legitimate user's browser stopped using the session, but the session itself was never destroyed.

This is the key distinction:

Client-side logout
→ browser forgets the credential

Server-side logout
→ credential itself becomes invalid

A secure logout should therefore invalidate the server-side session:

Logout requested
        ↓
Server destroys session record
        ↓
Session ID no longer maps to valid state
        ↓
Any future use of that ID is rejected

For token-based authentication, early invalidation may require an additional mechanism:

Stateless token
→ normally valid until expiration

Need immediate revocation
→ revocation list / denylist
→ token versioning
→ short-lived access tokens with controlled refresh

Password changes are especially important.

Consider:

Attacker steals session
        ↓
Victim discovers compromise
        ↓
Victim changes password
        ↓
Old stolen session remains valid
        ↓
Attacker still has access

Changing the password only protects future authentication attempts. It does not automatically invalidate a session credential that was already issued unless the application explicitly revokes or invalidates it.

A stronger response is:

Password changed
        ↓
Invalidate existing sessions
        ↓
Require authentication again
        ↓
Issue new sessions only after re-authentication

Applications may choose exceptions for the current device or trusted sessions, but that should be an explicit design decision rather than an accidental failure to invalidate anything.

6

Vulnerability

Session Hijacking

⚠️

Vulnerability

Session hijacking is the broad category covering attacks where an attacker obtains a valid session identifier belonging to another user.

Unlike predictable session IDs or session fixation, the attacker does not need to guess or manufacture the credential. The session already exists and is valid — the attacker's goal is simply to steal it.

Victim
→ valid authenticated session
        ↓
Session ID exposed or stolen
        ↓
Attacker obtains same credential
        ↓
Attacker sends it to application
        ↓
Server recognizes victim's session
        ↓
Attacker acts as the victim

Several common attack vectors can expose a session credential.

Network Interception:

If a session cookie is transmitted over an unencrypted HTTP connection, anyone able to observe that traffic may be able to capture it.

Browser
→ HTTP request
→ Cookie: session=abc123
        ↓
Network observer
→ captures session ID

The defense is to serve the application over HTTPS and mark session cookies with the Secure attribute:

Set-Cookie: session=abc123; Secure

Secure instructs the browser to send the cookie only over HTTPS connections.

Cross-Site Scripting:

XSS can expose a session identifier when attacker-controlled JavaScript runs in the application's origin.

Without HttpOnly:

document.cookie

may expose the session cookie to the injected script.

The attacker could potentially send that value elsewhere:

Injected JavaScript
        ↓
Reads accessible session cookie
        ↓
Attacker receives session ID
        ↓
Session hijacking

The HttpOnly attribute prevents JavaScript from directly reading the cookie:

Set-Cookie: session=abc123; HttpOnly

The distinction is important:

HttpOnly
→ prevents JavaScript from reading the cookie

HttpOnly
≠
prevents XSS

HttpOnly
≠
prevents malicious JavaScript from performing actions

An XSS vulnerability still needs to be fixed. HttpOnly simply limits one particularly valuable thing the injected script may be able to steal.

Session IDs in URLs

Session identifiers should never be placed in URLs:

https://example.com/account?session=abc123

URLs can be recorded in multiple places:

Browser history
Server access logs
Proxy logs
Analytics systems
Shared links
Referer headers

Session credentials should instead be carried through appropriately configured cookies or, where the architecture requires it, protected authorization headers.

CSRF and Ambient Authority:

CSRF does not normally steal the session identifier. Instead, it abuses the fact that the victim's browser may automatically attach that credential to a request.

Attacker-controlled page
        ↓
Triggers request to target application
        ↓
Browser may attach matching cookies
        ↓
Server sees authenticated request

The request may therefore execute with the victim's authority even though the victim never intended to perform that action.

The SameSite cookie attribute helps control when cookies are included in cross-site contexts:

Set-Cookie: session=abc123; SameSite=Lax

or, where the application's functionality permits:

Set-Cookie: session=abc123; SameSite=Strict

These settings help reduce CSRF exposure, though applications may still require additional CSRF protections depending on their architecture.

Defense in Depth

A properly configured session cookie might look like:

Set-Cookie: session=a9f3e7c1b2d84f...;
            Secure;
            HttpOnly;
            SameSite=Lax;
            Path=/

Combined with:

Cryptographically secure session IDs
        +
Session regeneration after login
        +
HTTPS everywhere
        +
Secure / HttpOnly / appropriate SameSite cookies
        +
No session IDs in URLs
        +
Real server-side session invalidation

These controls address different failure points. No single flag makes session management secure on its own.

You've completed Session Management

Great work — explore other topics to keep learning.