Phishing and session hijacking

A stolen session cookie lets an attacker walk in as you without ever knowing your password.

The idea

Phishing tricks you into handing over a credential or a live session — a lookalike page, a panic email, a fake "verify your account" link. The prize isn't always your password. If the page can read your session cookie, the attacker is already logged in.

A session cookie is a bearer token: whoever carries it is treated as you. That's why multi-factor authentication at login doesn't save you here — the session was already minted, and MFA is never re-checked on each request. Defence shifts from "prove who you are once" to "watch the live session and be able to kill it."

See it work

Press play, or step through it.

How it works

The server keeps sessions in a store it controls, hands out an opaque id in a locked-down cookie, and treats every request as untrusted until it re-reads that session and checks it still looks like the same client.

// 1. On login: mint a server-side session, hand back a hardened cookie.
res.cookie('sid', newSessionId(), {
  httpOnly: true,       // JS (and any XSS) cannot read the cookie
  secure:   true,       // only sent over HTTPS
  sameSite: 'Lax',      // not sent on cross-site POSTs
  maxAge:   15 * 60_000 // short TTL — re-issued as the user stays active
});
sessions.set(sid, { user: 'alice', ip, ua, role: 'user' });

// 2. On every request: look the session up server-side and re-check it.
function authenticate(req) {
  const s = sessions.get(req.cookies.sid);
  if (!s) return reject('no live session');     // revoked or expired
  if (impossibleTravel(s.ip, req.ip)) {         // anomaly fires
    sessions.delete(req.cookies.sid);           // revoke now
    return reject('session anomaly — re-auth required');
  }
  return s;
}

// 3. On login or privilege change: rotate the id so an old one is dead.
function rotate(oldSid) {
  const s = sessions.get(oldSid);
  sessions.delete(oldSid);
  const fresh = newSessionId();
  sessions.set(fresh, s);
  return fresh; // sid=a1f9... -> sid=b7c2...
}

Signals

SignalWhat it suggestsAction
Impossible-travel IP Same session is live in two places too far apart to be one person Revoke the session, force re-auth
New device fingerprint mid-session The cookie moved to a machine it wasn't issued to Step-up auth or revoke
Concurrent sessions from distant geos The same token is being replayed in parallel Kill all but the trusted session
Sudden privilege use A hijacked session reaching for admin or money actions Re-auth before the action, alert
User-agent flip Browser or OS string changed under a stable session Rebind or revoke, raise the risk score

Watch out for

Worked example

Alice logs in from her laptop in NYC; the server mints sid=a1f9 in an HttpOnly; Secure cookie and stores { user: alice, ip: 203.0.113.10 }. She clicks a lookalike "billing alert" link; the page quietly captures and ships the cookie value. Four minutes later sid=a1f9 replays from a datacenter IP in another country. The session is technically valid, but the impossible-travel rule fires: four minutes is not enough time to cross continents. The server revokes a1f9, forces Alice to re-authenticate, and rotates her to a fresh sid=b7c2 bound to her real device. The attacker's next replayed request hits a dead session and gets a 401.

Check yourself

True or false: requiring MFA at login fully protects you against a stolen session cookie.