Skip to content

[Security] Session Fixation Vulnerability and bcrypt Password Hash Leaked in Login Response #375

@advikdivekar

Description

@advikdivekar

Description

A HIGH severity security vulnerability exists in backend/routes/auth.js at line 34 and backend/config/passportConfig.js. Two issues compound: (1) the session ID is never regenerated after successful authentication, enabling session fixation attacks; (2) deserializeUser returns the full Mongoose document including the bcrypt-hashed password, which is then serialised into the login response JSON via req.user.

Impact

Session fixation: an attacker who can plant a known session ID into a victim's browser (via cookie injection from a related subdomain or briefly shared access) becomes fully authenticated as that victim after the victim logs in — no password required.

Password hash leak: the bcrypt hash present in the POST /api/auth/login response enables offline dictionary attacks. Anyone who can read network traffic, API logs, or browser DevTools obtains a crackable credential.

Steps to Reproduce

  1. Make any unauthenticated request to the server (e.g., GET /api/auth/logout) and record the connect.sid cookie value.
  2. Log in with valid credentials at POST /api/auth/login.
  3. Inspect the JSON response body — it contains a password field with the full bcrypt hash.
  4. After login, inspect connect.sid — the value is unchanged from step 1, confirming no session regeneration occurred.

Expected Behaviour

On successful login the server must issue a brand-new session ID. The login response must contain only safe fields (id, username, email) — never a password hash or any credential material.

Proposed Fix

Refactor the login route to regenerate the session before establishing the authenticated context, and fix deserializeUser to never load the password hash.

// backend/routes/auth.js — replace login route
router.post("/login", validateRequest(loginSchema), (req, res, next) => {
    passport.authenticate('local', (err, user, info) => {
        if (err) return next(err);
        if (!user) return res.status(401).json({ message: info?.message || 'Invalid credentials' });

        req.session.regenerate((err) => {
            if (err) return next(err);

            req.logIn(user, (err) => {
                if (err) return next(err);
                res.status(200).json({
                    message: 'Login successful',
                    user: { id: user.id, username: user.username, email: user.email },
                });
            });
        });
    })(req, res, next);
});
// backend/config/passportConfig.js — fix deserializeUser
passport.deserializeUser(async (id, done) => {
    try {
        const user = await User.findById(id).select('-password');
        done(null, user);
    } catch (err) {
        done(err, null);
    }
});

Files affected: backend/routes/auth.js, backend/config/passportConfig.js

Labels

type:security level:advanced gssoc:approved

Please assign this issue to me under GSSoC 2026. I will open a PR with a complete fix covering all affected files, proper test coverage, and verification steps.

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions