Skip to content

Bug: TOCTOU race condition in session creation allows concurrent login bypass #3945

Description

@Siddh2024

Description

The createSession function in lib/sessionManager.js (lines 16-22) has a classic Time-of-Check-Time-of-Use (TOCTOU) race condition. It reads existing sessions, deletes them, then creates a new one — but these three steps are not atomic.

const existingSessions = await redis.smembers(`user:sessions:${userId}`);
if (existingSessions && existingSessions.length > 0) {
  const pipeline = redis.multi();
  existingSessions.forEach((sid) => pipeline.del(`session:${sid}`));
  pipeline.del(`user:sessions:${userId}`);
  await pipeline.exec();
}

Impact

If two login requests arrive concurrently for the same user:

  1. Request A reads user:sessions:{userId} — empty (1 session)
  2. Request B reads user:sessions:{userId} — empty (the same 1 session)
  3. Both A and B create new sessions and add themselves to the set
  4. Both logins succeed, violating the "single session" policy

A user can have multiple active sessions simultaneously, defeating the concurrent-login prevention mechanism.

File

  • lib/sessionManager.js:16-22createSession function

Suggested Fix

Use a distributed lock (e.g., via the existing lockManager.js) around the read-then-delete-then-create sequence, or use a Redis Lua script that atomically clears old sessions and creates the new one in a single operation.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions