Skip to content
pwnsy
web-securityintermediate#session-hijacking#web-security#authentication#cookies#appsec

Session Hijacking: Stealing & Replaying Session Tokens

How session hijacking works: cookie theft, XSS-to-session takeover, token replay, and the defenses that keep a stolen session unusable.

When you log in to a web application, the server hands your browser a token, usually a cookie, that proves who you are for the rest of your visit. Every request after that carries the token instead of your password. The application trusts the token completely. That trust is the whole target of session hijacking.

Session hijacking is the theft and reuse of that token. An attacker who obtains it does not need your username, your password, or your second factor. They present the token, the server sees a valid session, and they are you for as long as the session lasts.

What a session actually is

HTTP has no memory. Each request stands alone, so the server needs a way to recognise a returning user. The standard answer is a session identifier: a long random value the server generates at login and stores against your account. Your browser sends it back on every request, normally in a cookie, and the server looks it up to know it is still you.

The token is a bearer credential. Whoever holds it is treated as the account. There is no further check that the holder is the same person who logged in, which is exactly why stealing the token is enough.

On MITRE ATT&CK, theft of this credential is Steal Web Session Cookie (T1539), and it sits alongside credential theft precisely because the outcome is the same: account takeover.

How tokens get stolen

Attackers reach session tokens through a handful of well-worn routes.

Cross-site scripting (XSS). If an attacker can run JavaScript in your browser on the target site, that script can read document.cookie and send it anywhere. XSS is the classic engine of session theft, which is why it and session hijacking are so often discussed together. A single reflected or stored script turns into full account takeover.

Network interception. On any unencrypted hop, a party on the path can read the cookie as it travels. This is the old sidejacking attack. It is far less common now that transport encryption is near-universal, but any page served over plain HTTP, or any cookie not marked to require encryption, reopens the door.

Malware and infostealers. Modern infostealer malware skips the network entirely and reads cookies straight out of the browser's storage on a compromised machine. This has become a mass-market attack, because a stolen session cookie sidesteps both the password and any multi-factor prompt. The victim already passed those checks; the malware just takes the result. See What Is an Infostealer for how that trade works.

Predictable or leaked tokens. If session IDs are short, sequential, or generated from something guessable, an attacker can produce valid ones without stealing anything. Tokens can also leak through referrer headers, logs, or URLs when an application puts the session ID in the query string instead of a cookie.

Fixation is the mirror image

There is a related attack that runs the theft backwards. Instead of stealing a token the victim already has, the attacker plants a token they already know and waits for the victim to authenticate it. That is session fixation, and it works whenever an application keeps the same session ID before and after login. The defense overlaps heavily with hijacking, and the two are worth reading together. See Session Fixation Explained.

What replay looks like

Once a token is in hand, the attack is trivial. The attacker sets the stolen cookie in their own browser or drops it into a request with a tool, and sends a request to the application. The server validates the token, finds a live session, and serves the victim's authenticated pages. No login screen, no prompt.

Theft routeWhere it happensPrimary defense
Cross-site scriptingVictim's browserHttpOnly cookies, output encoding, CSP
Network interceptionNetwork pathHTTPS everywhere, Secure cookie flag
Infostealer malwareVictim's deviceEndpoint security, token binding, short lifetimes
Predictable tokenAttacker's machineHigh-entropy random session IDs
Cross-site leakageThird-party siteSameSite cookie attribute
A stolen session skips your second factor

Multi-factor authentication protects the login. It does nothing for a session that is already authenticated. An attacker replaying a stolen token never sees the login screen, so the second factor is never asked for. This is why cookie theft has become the infostealer economy's favourite product, and why token protection matters as much as strong login.

Tokens as JWTs

Many modern applications carry session state in a signed JSON Web Token rather than an opaque server-side session ID. That changes the theft surface: a stolen JWT is just as replayable, and a badly validated one can sometimes be forged outright. The hijacking principles here still apply, plus a set of signature and validation issues specific to the format. See JWT Attacks Explained.

A worked example of a hijack

Tracing a single hijack end to end makes the abstract routes concrete. Consider the most common modern path, an XSS bug feeding cookie theft, described conceptually rather than as working exploit code.

A web application has a comment field that echoes user input back onto the page without proper encoding. An attacker posts a comment containing a script. When any other logged-in user loads the page, their browser runs that script in the context of the application's origin. Because the script runs as the site, it has access to everything the site's own JavaScript can reach, including the session cookie, unless that cookie is protected.

The script reads the session cookie and sends it to a server the attacker controls, typically by appending it to a request for an image or a background fetch to an external host. The victim notices nothing. The page renders normally, the comment looks ordinary, and the theft happens in the background in milliseconds.

The attacker now holds the victim's session token. They open their own browser, set a cookie for the target domain with the stolen value, and load the application. The server receives a request carrying a valid, live session identifier, looks it up, finds the victim's authenticated session, and serves the victim's account pages. The attacker never saw a login form, never entered a password, and was never prompted for a second factor. From the server's perspective, the returning request is indistinguishable from the real user.

HttpOnly breaks this exact chain

The step that makes the whole example work is the script reading the cookie. Marking the session cookie HttpOnly removes the cookie from the reach of JavaScript entirely, so document.cookie cannot see it and the theft step fails even though the XSS bug still exists. That is why HttpOnly is the single highest-value session setting: it severs the most common theft route at its narrowest point, independent of whether every XSS bug has been found and fixed.

Where the token lives, and why it matters

A session token can ride in several places, and each location changes the theft surface. Knowing which one an application uses tells you which defenses are relevant.

Storage locationHow the token is sentMain exposure
Cookie without HttpOnlyAutomatically on every requestReadable by any script on the page
Cookie with HttpOnlyAutomatically on every requestHidden from scripts, still sent in requests
Local storage or session storageAttached by application codeAlways readable by scripts, a poor choice for tokens
URL query stringIn the address itselfLeaks through logs, referrer headers, and shared links
Authorization headerAttached by application codeDepends on where the code keeps the token

The pattern in that table is that any location a script can read is a location an XSS bug can steal from. Cookies with HttpOnly are the only common option that keeps the token out of script reach while still sending it automatically. Storing a session token in local storage is a frequent mistake precisely because local storage is always script-readable, so an XSS bug hands the attacker the token directly. Putting a session identifier in the URL is worse still, because URLs end up in server logs, browser history, and the referrer header sent to other sites, leaking the token through channels nobody is watching.

Why defenses fail when they are half-applied

The controls against session hijacking work as a set, and each has a specific gap when it stands alone. Understanding the gaps explains why layering is not optional.

HttpOnly without Secure leaves the network path open. The cookie is safe from scripts, but if it is sent over an unencrypted connection, anyone on the path can read it in transit. Secure without HttpOnly leaves the script path open. The cookie only travels encrypted, but an XSS bug reads it before it ever hits the network. Neither flag covers the other's gap.

SameSite reduces cross-site leakage but does nothing about theft that happens on the site itself. It stops the cookie from being attached to requests originating from other sites, which blunts cross-site request forgery and some leakage, yet an XSS bug running on the target origin is not a cross-site context, so SameSite does not stop it.

High-entropy tokens defeat guessing but not theft. A long random session identifier cannot be enumerated, which closes the predictable-token route, but a stolen token is stolen regardless of how random it was. Entropy protects against one route and is silent on the others.

Short lifetimes and revocation shrink the value of a stolen token without preventing the theft. If a token expires quickly or can be invalidated server-side, a thief has a narrow window to use it, which is genuinely valuable. It does not stop the token from being taken, so it works only in combination with the controls that make theft harder in the first place.

The conclusion is that no single setting is sufficient. HttpOnly, Secure, SameSite, high entropy, rotation, and short revocable lifetimes each close a different route, and an attacker only needs one open route. The defense is the full set applied together.

Common mistakes and misconceptions

A few recurring errors leave sessions exposed even in applications that look carefully built.

The first is trusting multi-factor authentication to protect the session. MFA protects the login event. Once a session exists, it is a bearer token, and a stolen one is replayed without any login, so the second factor is never consulted. Teams sometimes treat MFA as a complete answer to account takeover and neglect session protection, which leaves the exact gap that infostealers exploit.

The second is storing the token in local storage for convenience. Frameworks and tutorials sometimes suggest keeping a token in local storage so client code can read it. That decision makes the token permanently script-readable and turns any XSS bug into instant account takeover. A cookie with HttpOnly, Secure, and SameSite is the safer default for a session token.

The third is regenerating the session identifier at login but nowhere else. Regeneration at login defeats fixation, but privilege changes also matter. Stepping into an admin area or re-authenticating for a sensitive action without rotating the token lets an earlier, lower-privilege session ride into a higher-privilege state. Rotation belongs at every trust boundary, with the first login being just one of them.

The fourth is clearing the cookie at logout without destroying the session server-side. If logout only removes the cookie from the browser, the token itself is still valid on the server, and anyone who captured it earlier can keep using it. Logout must invalidate the session in server-side state, not merely clear the client's copy.

How to detect a hijacked session

Prevention is the priority, and detection is the backstop for the cases prevention misses. Several signals point to a session being used by someone other than its owner.

  • A single session used from two distant locations at once. A session active from two far-apart network locations within a span too short to travel between them suggests the token is being shared, which is the signature of a stolen cookie in use.
  • An abrupt change in device or client fingerprint mid-session. A session that begins on one browser and operating system and continues on a different one, without a fresh login, is a strong indicator the token moved to another machine.
  • Sudden sensitive actions without re-authentication. A session that jumps straight to changing account settings, adding a recipient, or exporting data, with no matching step-up authentication, is worth flagging.
  • Session activity that outlasts the user. Requests continuing on a session after the user's own device has gone idle or logged out can indicate a second holder of the same token.
  • A spike in requests to session-bearing endpoints from unusual infrastructure. Access arriving from hosting providers or anonymizing infrastructure, rather than the residential paths a user normally comes from, can mark replayed tokens.

None of these is conclusive alone, and each can have an innocent explanation, so the value is in correlation and in tying a suspicious signal to an action that re-verifies the user. Binding the session to context and re-authenticating for sensitive actions turns several of these signals into automatic friction for a thief.

The practical challenge is false positives. Users legitimately switch networks, travel, and move between devices, so any single signal fires often enough that acting on it in isolation harms real users. The workable approach is to weight signals together and to respond with graduated friction rather than an immediate block. A mild anomaly might trigger a re-authentication prompt for the next sensitive action, while a strong combination, such as a distant location paired with a changed device fingerprint and an immediate attempt to change account settings, justifies suspending the session outright. Tuning that balance is the real work of session-hijacking detection, because the goal is to make a stolen token expensive to use without making a normal session annoying to hold.

How to defend against session hijacking

The goal is twofold: make the token hard to steal, and make a stolen token expensive to use. Layer these controls rather than relying on any single one.

  1. Mark cookies HttpOnly. This blocks JavaScript from reading the cookie, which removes XSS as a token-theft route even when an XSS bug exists. It is the single highest-value setting.
  2. Mark cookies Secure. The browser then sends the cookie only over encrypted connections, closing the network-interception path.
  3. Set SameSite. SameSite=Lax or Strict stops the cookie from riding along on cross-site requests, cutting off cross-site leakage and blunting related cross-site attacks.
  4. Generate high-entropy tokens. Use a long, cryptographically random session ID so tokens cannot be guessed or enumerated. Let your framework's session manager produce it rather than rolling your own.
  5. Regenerate the session ID on login and on privilege change. A fresh token at each trust boundary defeats fixation and limits how long any earlier token is valid.
  6. Keep sessions short and revocable. Enforce absolute and idle timeouts, and keep server-side state so you can invalidate a session immediately on logout or on suspicion of theft. A token that expires in minutes is worth little to a thief.
  7. Bind the session to context where you can. Tying a session to properties that are hard to replay, and re-authenticating for sensitive actions, raises the cost of replay even after theft.
  8. Fix the XSS. Since XSS is the main theft engine, output encoding, input validation, and a strict Content Security Policy do double duty as session-protection controls.
Defend the login and the session separately

Strong authentication and strong session management are two different jobs. Multi-factor login stops password attacks; HttpOnly, Secure, SameSite cookies plus rotation and short lifetimes stop token attacks. Do both. A hardened login in front of a sloppy session is still a full account takeover waiting to happen.

Session hijacking rewards attackers because it converts one stolen string into complete account access, no credentials required. The economics are stark: our Exploit Intelligence dashboard tracks how quickly credential and session-theft techniques move from disclosure to active use, and stolen-cookie access consistently sits among the fastest to be weaponised. Treat the session token as the sensitive credential it is, harden the cookie, rotate it at every trust boundary, and keep its lifetime short, and a token that does get stolen is worth little by the time anyone tries to use it.

Frequently asked questions

Does HTTPS alone stop session hijacking?

No. HTTPS closes the network-interception route by encrypting the token in transit, which is important, but it does nothing about the other routes. An XSS bug reading the cookie, an infostealer lifting it from disk, or a predictable token generated on the server are all untouched by transport encryption. HTTPS is necessary and not sufficient, which is why it sits alongside HttpOnly, SameSite, high entropy, and short lifetimes rather than replacing them.

If I have multi-factor authentication, why do I still need session protection?

Because the two protect different moments. Multi-factor authentication verifies the person at login. The session token that login produces is a bearer credential used for the rest of the visit, and a stolen token is replayed without any login at all, so the second factor is never asked for. This is exactly why infostealers target cookies: a stolen session sidesteps both the password and the second factor by arriving after both were already satisfied.

Is storing a session token in local storage safe?

It is a poor choice. Local storage is always readable by JavaScript, so any cross-site scripting bug can read the token directly and no flag can hide it. A cookie marked HttpOnly is kept out of script reach while still being sent automatically, which is why an HttpOnly, Secure, SameSite cookie is the safer default for a session token than local storage.

How is session hijacking different from session fixation?

In hijacking, the attacker steals a token the victim already holds and replays it. In fixation, the attacker plants a token they already know and waits for the victim to authenticate it. Hijacking happens after login, fixation before it. The defenses overlap heavily, and regenerating the session identifier at login helps against both. The fixation case is covered in full in Session Fixation Explained.

Can a stolen session token be used forever?

Only for as long as the session remains valid. If the application enforces short absolute and idle timeouts and keeps server-side state that can be invalidated, a stolen token stops working when the session expires or when the session is revoked. If sessions are long-lived and cannot be revoked server-side, a stolen token can be usable for a long time, which is why short, revocable sessions are a core control.

Does logging out protect me if my token was already stolen?

Only if logout destroys the session on the server. If logout merely clears the cookie in your browser, the token itself is still valid and an attacker who captured it earlier can keep using it. A correct logout invalidates the session in server-side state, which is what actually revokes a token that has already been stolen.

Are JWT sessions safer against hijacking?

Not inherently. A stolen JSON Web Token is just as replayable as a stolen opaque session identifier, and a JWT held in a script-readable location is exposed to the same XSS theft. JWTs add their own signature and validation concerns on top. The hijacking defenses still apply, plus the format-specific issues covered in JWT Attacks Explained.

Sources & further reading

Sharetwitterlinkedin

Related guides