Detection Engineering

Session Hijacking Detection

Session hijacking detection after MFA. Refresh-token reuse, device drift, and impossible travel, with SPL, KQL, and Sigma analytics plus a triage runbook.

A dark scene showing a cyan session token thread splitting into a second red thread, representing session hijacking detection

Session hijacking detection is the discipline of catching an attacker who already holds a valid session. There is no failed login to alert on, no password spray to threshold, and no MFA prompt to deny, because authentication already succeeded and the attacker simply inherited the result. The stolen artifact is a cookie, a bearer token, or a refresh token, and to your application it is indistinguishable from the real user. This guide ships the analytics that make it distinguishable anyway.

Session hijacking maps to MITRE ATT&CK T1539 — Steal Web Session Cookie and T1550.004 — Use Alternate Authentication Material: Web Session Cookie. It is the technique that makes “we deployed MFA” an incomplete answer, and it is why phishing detection beyond DMARC matters even in an MFA-enforced estate.

What is session hijacking?

Session hijacking is the theft and reuse of an authenticated session artifact so that an attacker inherits an identity without ever learning its password. The victim authenticated correctly, satisfied MFA, and received a session. The attacker steals that session and replays it.

The defining property, and the reason it defeats most identity monitoring, is that there is no new login event to detect. Detection stacks are overwhelmingly built around authentication: failed logins, password spraying, impossible-travel-between-logins, new-device sign-ins. A hijacked session generates none of those. It generates ordinary API traffic from an already-trusted principal.

The artifact varies by architecture, and each variety has a different theft path and a different useful lifetime:

ArtifactTypical lifetimeHow it is stolenWhy it matters
Session cookieHours to weeksInfostealer malware, XSS, physical accessLong-lived, often no binding at all
Access token (JWT)Minutes to an hourProxy phishing, log leakage, SSRFShort life limits damage window
Refresh tokenDays to monthsInfostealer, token-store compromiseThe real prize — mints new access tokens
OAuth grant / consentUntil revokedIllicit consent phishingSurvives password reset entirely
Cloud session tokenHoursInstance metadata SSRF, CI log leakInherits full workload permissions

Note the fourth row in particular. An OAuth grant obtained through consent phishing survives a password reset and an MFA re-enrollment, because it is not a credential at all. It is a standing authorization. That is the case handled in OAuth misconfiguration defensive review.

How do attackers steal sessions?

Four vectors dominate, and they demand different telemetry. Knowing which one you are exposed to determines which detection below earns its keep.

Infostealer malware is now the volume leader. Commodity stealers harvest browser cookie stores and token caches from an endpoint, then sell them in bulk. The victim notices nothing because nothing on their machine breaks. The stolen cookie appears from a different IP, different device, and different browser build, which is precisely what the device-drift analytic below is built to catch.

Adversary-in-the-middle phishing proxies the real login page in real time. The victim sees a genuine site, enters credentials, and completes MFA against the legitimate identity provider. The proxy relays everything and keeps the resulting session cookie. This is the vector that makes phishing-resistant authentication necessary but not sufficient.

Token leakage through logs and telemetry is the self-inflicted one. Bearer tokens land in access logs, error traces, APM payloads, or CI job output, and anyone with log-read access inherits every session recorded there. This overlaps directly with secrets detection and is the vector most likely to be discovered internally rather than exploited externally.

Authorization bypass in the session-validation layer is rarer but severe, because it forges rather than steals. CVE-2025-29927, a Next.js flaw rated CVSS 9.1, allowed authorization checks implemented in middleware to be bypassed outright with a crafted header. Where session validity is decided in a single middleware layer, a bypass there is equivalent to holding every session at once. This is T1606 — Forge Web Credentials territory, and it argues for validating authorization at the resource, not only at the edge.

What telemetry do you need to detect session hijacking?

Every analytic in this post fails silently without the right fields, and this is the step most teams skip. Before writing a rule, confirm you are collecting the following.

RequirementWhy it is load-bearingCommon failure
Stable session ID on every eventCorrelates auth to later resource accessOnly logged at login, not on API calls
Token issuance and redemption recordsMakes reuse detection possible at allRefresh redemptions not logged
jti or equivalent token identifierDistinguishes reissue from replayTokens logged without a unique ID
Device fingerprint at auth and on requestsDetects mid-session device changeCaptured at login only
Source IP plus resolved ASN and geoPowers impossible travel and ASN driftBehind a proxy that overwrites the client IP
Explicit re-authentication eventsDistinguishes legitimate context changeStep-up auth not emitted as its own event
Retention of at least 30 daysSessions outlive short retention windows7-day retention on identity logs

The two that break most deployments are the third and the fifth. Without a per-token identifier you cannot tell a legitimate reissue from a replay, and the reuse detection becomes impossible rather than merely noisy. And if your load balancer or CDN overwrites the client IP without populating a forwarded-for header you trust, every geo and ASN analytic silently evaluates your own edge nodes and never fires.

How to detect session hijacking

Three analytics, in descending order of fidelity. Deploy the first one even if you deploy nothing else.

Refresh-token reuse after rotation

This is the highest-fidelity identity detection available, and it deserves more attention than it gets. When refresh tokens rotate on every use, each token is strictly single-use: redeeming it issues a replacement and invalidates the original. A correctly behaving client therefore never presents a redeemed token twice.

So if a redeemed token is presented again, exactly two readings exist, and both are actionable. Either a legitimate client raced or crashed mid-rotation and retried with a stale copy, or somebody holds a copy they should not. You cannot distinguish them from the token alone, which is why the correct response is not to alert and investigate but to revoke the entire token family immediately. Every holder re-authenticates. The legitimate user experiences one re-login. The attacker loses persistence.

SPL Refresh-Token Reuse After Rotation (Stolen-Token Proof)
index=identity sourcetype=auth:token_redemption
| stats count AS redemptions,
        dc(src_ip) AS distinct_ips,
        dc(device_id) AS distinct_devices,
        earliest(_time) AS first_use,
        latest(_time) AS last_use,
        values(user_agent) AS agents
        by token_jti, user_id, token_family_id
| where redemptions > 1
| eval seconds_apart = last_use - first_use
| eval verdict = if(distinct_devices > 1 OR seconds_apart > 60,
                    "THEFT_LIKELY", "CLIENT_RACE_POSSIBLE")
| table user_id, token_family_id, token_jti, redemptions,
        distinct_ips, distinct_devices, seconds_apart, verdict, agents
| sort - redemptions

The verdict field encodes the one genuine ambiguity. A client race resolves in under a second from the same device, because the retry is the same process recovering from a dropped response. Two redemptions minutes apart from different devices is theft. Revoke the family in both cases and use the verdict to decide whether to page someone.

Device-binding drift mid-session

The second-strongest signal is a session that changes device without changing identity. A stolen cookie is replayed from the attacker’s browser on the attacker’s machine, so the fingerprint moves while the session ID stays constant.

KQL Session Continuation From a New Device Fingerprint
SigninLogs
| where TimeGenerated > ago(24h)
| project TimeGenerated, UserPrincipalName, SessionId,
          DeviceFingerprint = tostring(DeviceDetail.deviceId),
          Browser = tostring(DeviceDetail.browser),
          OS = tostring(DeviceDetail.operatingSystem),
          IPAddress, ASN = tostring(NetworkLocationDetails)
| where isnotempty(SessionId)
| summarize FirstSeen = min(TimeGenerated),
            Fingerprints = make_set(DeviceFingerprint, 10),
            Browsers = make_set(Browser, 10),
            OSes = make_set(OS, 10),
            IPs = make_set(IPAddress, 20),
            ASNs = make_set(ASN, 10)
        by SessionId, UserPrincipalName
| extend FingerprintCount = array_length(Fingerprints),
         OSCount = array_length(OSes)
| where FingerprintCount > 1 or OSCount > 1
| join kind=leftanti (
    SigninLogs
    | where TimeGenerated > ago(24h)
    | where AuthenticationRequirement == "multiFactorAuthentication"
    | project SessionId
  ) on SessionId
| order by FingerprintCount desc

The leftanti join is the part that makes this usable rather than noisy. It drops any session that saw a genuine re-authentication, because a user who legitimately re-authenticated on a new laptop is not an incident. What remains is device change without a corresponding auth event, which is the actual anomaly. An operating-system change mid-session is especially strong; users switch browsers, they do not switch from macOS to Linux inside one session.

Impossible travel within a single session

Impossible travel is a familiar analytic, but it is almost always applied between login events. Applied within one session, it becomes considerably more meaningful, because a single session legitimately belongs to a single physical person.

Sigma Impossible Travel Within a Single Session Lifetime
title: Impossible Travel Within a Single Session Lifetime
id: 8c1f4a26-7d3e-4b91-9a02-3f6d5e8b7c41
status: experimental
description: >
  Detects a single authenticated session presenting from two geographic locations
  whose separation cannot be covered in the elapsed time, indicating the session
  artifact is held by more than one party.
references:
  - https://attack.mitre.org/techniques/T1539/
  - https://attack.mitre.org/techniques/T1550/004/
author: Colson
date: 2026/07/28
logsource:
  category: application
  product: identity_provider
detection:
  selection:
    event_type: 'resource_access'
    session_id: '*'
  timeframe: 1h
  condition: selection | count(distinct_country) by session_id > 1
fields:
  - session_id
  - user_id
  - src_ip
  - src_country
  - src_asn
  - user_agent
falsepositives:
  - Corporate VPN egress in a different country than the user
  - Mobile carrier CGNAT assigning geographically distant egress addresses
  - Users legitimately switching between VPN and local network mid-session
level: high

Treat the Sigma rule as the portable statement of intent and implement the velocity maths in your SIEM, since Sigma’s correlation grammar cannot express great-circle distance over elapsed time. The threshold that works in practice is roughly 900 km/h, which sits above commercial aviation cruise speed and therefore treats a genuine flight as plausible rather than impossible.

Which false positives will you actually see?

This is the section that determines whether your detection survives its first month. Every rule above fires on legitimate behaviour in specific, predictable ways. Handle them deliberately.

False positiveWhich rule it hitsWhy it happensResolution
Client retry after dropped responseRefresh reuseNetwork failure mid-rotationAllow a sub-second same-device grace window; still revoke the family
Corporate VPN egress abroadImpossible travelEgress node in another countryAllowlist known corporate ASNs and egress ranges
Mobile CGNAT geo jitterImpossible travelCarrier NAT pools span regionsExclude known mobile ASNs from geo velocity
Browser auto-updateDevice driftUser-agent string changesFingerprint on stable attributes, not full UA string
Multi-profile browser useDevice driftDifferent profile, same machineInclude a hardware-stable component in the fingerprint
Legitimate device migrationDevice driftUser genuinely got a new laptopThe leftanti re-auth join already suppresses this
Shared service accountsAll threeMany humans, one identityExclude and migrate to per-principal identities
Security scanner trafficDevice driftScanner replays session tokensAllowlist scanner source ranges explicitly

The last row on shared service accounts is worth acting on rather than suppressing. A service account used by six engineers is indistinguishable from a hijacked session by construction, and no analytic will fix that. Migrating to per-principal identity is the real remediation, and the detection noise is simply telling you the truth about your identity model.

How do you triage a suspected hijacked session?

Detection without a runbook produces alerts nobody can action. Work these in order, because the order is chosen to contain first and investigate second.

  1. Revoke the token family immediately. Do not investigate first. Revocation costs the legitimate user one re-login; delay costs you the attacker’s entire dwell time. This is the rare case where containment genuinely precedes analysis.
  2. Pull every action taken under the session ID. You logged the session ID on resource events, so scope is a single query rather than an archaeology project.
  3. Separate the two device fingerprints. Establish which was the user and which was not by comparing against the user’s 30-day baseline, then attribute each action to one of them.
  4. Check for persistence established under the session. Look specifically for new OAuth grants, new API keys, new MFA methods enrolled, mail-forwarding rules, and new registered devices. This is the step that determines whether revocation actually ended the intrusion.
  5. Check for lateral movement. A hijacked session on one platform frequently precedes lateral movement elsewhere via SSO.
  6. Determine the theft vector. Endpoint infostealer, phishing proxy, or leaked token in logs. The answer changes the remediation entirely and is worth the time.
  7. Force re-enrollment if the endpoint is implicated. If a stealer took the cookie, it took everything else in that browser profile too. Treat the whole profile as compromised.
  8. Rotate anything the session could reach. Application secrets, API keys, and credentials accessible to that identity.

Step 4 is the one most often skipped and most often decisive. An attacker who used a hijacked session to enroll their own MFA method or mint an API key retains access after you revoke every token, and you will conclude the incident is closed while it is open.

How to test your session hijacking detection

In a lab environment, against systems you own or are explicitly authorized to test:

  1. Authenticate, capture the refresh token, redeem it once legitimately, then present the redeemed token a second time. Confirm the reuse analytic fires and the family revokes.
  2. Copy a valid session cookie into a second browser on a different operating system. Confirm device-drift fires and that the leftanti join does not suppress it.
  3. Re-authenticate legitimately on a genuinely new device. Confirm this does not alert, so you know the suppression works in the direction you intended.
  4. Route a session through a VPN egress in another country mid-session. Confirm impossible travel fires, then confirm your corporate-ASN allowlist suppresses the known-good case.
  5. Verify the analytics still fire when the client is behind a proxy, which is where the client-IP field most often turns out to be your own load balancer.

Item 3 matters more than it looks. A detection that fires on everything is operationally identical to no detection, and confirming the negative case is how you know the rule is discriminating rather than merely alerting.

How to prevent session hijacking

Detection is the backstop. The architecture is what actually constrains the attack, and the controls below are ordered by how much they reduce the value of a stolen artifact.

Beyond the token mechanics, four controls carry disproportionate weight:

  • Validate authorization at the resource, not only at the edge. CVE-2025-29927 is the argument. Where a single middleware layer decides every session’s validity, a bypass there is equivalent to holding every session simultaneously. Defence in depth means the resource re-checks.
  • Set cookies correctly and boringly. HttpOnly prevents script access, Secure prevents cleartext transmission, and SameSite=Lax or Strict blunts cross-site replay. These are free and still frequently missing.
  • Bind sensitive operations to re-authentication, not to session age. “Authenticated within the last 30 minutes” is weaker than “prove possession of the key right now.”
  • Constrain OAuth grant scope and expire grants. A standing grant outlives password resets; a scoped, expiring one does not.

Which session control should you deploy first?

ControlStopsCost to implementUser frictionDeploy when
Short access-token TTLLong-lived replayLowNoneAlways, first
Refresh rotation + family revocationPersistent theftMediumOne re-login on triggerAlways, second
Device bindingCross-device replayMediumOccasional re-authYou have a stable client
Step-up on sensitive actionsDamage from any hijackMediumPrompt on high-risk actionsYou hold money or data
Full token binding (DPoP/mTLS)Nearly all replayHighNone once workingRegulated or high-value
Continuous access evaluationPost-issuance revocation lagHighNoneYou are on a supporting IdP

Start at the top. Short lifetimes plus rotation with family revocation deliver most of the available risk reduction for a fraction of the effort of token binding, and they are the two that make the detections in this post possible in the first place.

Common session hijacking detection mistakes

  • Assuming MFA covers it. It covers the authentication event and nothing after it.
  • Alerting on refresh reuse instead of revoking. Reuse is proof, not a lead. Revoke first.
  • Logging session IDs only at login. This severs the correlation every analytic needs.
  • Fingerprinting on the raw user-agent. Guarantees mass false positives on browser update day.
  • Running impossible travel between logins only. Within-session is the stronger form.
  • Trusting the client IP behind a proxy. Verify you are not geolocating your own edge.
  • Closing the incident at revocation. Check for MFA enrollment, API keys, and OAuth grants.
  • Ignoring service accounts. They defeat the model rather than merely adding noise.

Session hijacking detection checklist

  1. Carry a stable session identifier onto every resource-access event, not just authentication.
  2. Log token issuance and redemption with a unique per-token identifier.
  3. Deploy refresh-token rotation and alert on any reuse of a redeemed token.
  4. Revoke the whole token family automatically on reuse rather than queuing an investigation.
  5. Capture a device fingerprint built from update-stable attributes, on every request.
  6. Detect device or OS change mid-session, suppressed by a genuine re-authentication join.
  7. Run impossible travel within a single session at roughly a 900 km/h velocity threshold.
  8. Allowlist corporate VPN and mobile-carrier ASNs before enabling geo analytics.
  9. Verify the client IP is the real client and not your load balancer.
  10. Set access-token TTL to fifteen minutes and bind refresh tokens to device plus IP range.
  11. Require step-up re-authentication for MFA changes, API-key creation, and payment changes.
  12. Validate authorization at the resource layer, not only in edge middleware.
  13. Retain identity telemetry for at least 30 days so sessions do not outlive their evidence.
  14. Rehearse the triage runbook, especially the persistence check at step 4.

The takeaway

Session hijacking detection works because a stolen artifact cannot carry its original context. The token can be copied; the device, the location, and the single-use guarantee cannot. Detect refresh-token reuse first, because it is proof rather than suspicion, then layer device drift and within-session impossible travel. Revoke on evidence instead of investigating toward certainty. Then remove most of the value from theft with short lifetimes, rotation, binding, and step-up authentication. Continue with OAuth misconfiguration defensive review, JWT misconfiguration detection, and credential stuffing detection, or browse the full Detection Engineering pillar.

Training & tools referenced

Disclosure: Some links below are affiliate links. If you buy through them, darkpwn may earn a commission at no extra cost to you. We only recommend training and tools we actually use in our own lab, and affiliate links never influence editorial coverage.

  • TryHackMeAuthorized labs to practice identity attack analysis and session securitySecurity Training
    Start training
  • YubiKeyPhishing-resistant hardware keys for step-up re-authenticationHardware Security
    View hardware keys

Frequently asked questions

What is session hijacking?

Session hijacking is the theft and reuse of an authenticated session artifact — a cookie, bearer token, or refresh token — so the attacker inherits an already-authenticated identity without ever knowing the password. Because authentication already happened, the stolen session sails past MFA entirely. The login event looks normal because there is no new login event.

How do you detect session hijacking?

Detect it on the signals a stolen token cannot carry with it. The strongest is refresh-token reuse after rotation, which is mathematically impossible for a legitimate client and therefore near-zero false positive. Then layer device-fingerprint drift mid-session, impossible travel within one session lifetime, and ASN or user-agent changes without a re-authentication event.

Does MFA stop session hijacking?

No. MFA protects the authentication event, and session hijacking happens after that event has already succeeded. A stolen session cookie or refresh token represents completed authentication, so replaying it never triggers an MFA prompt. Only token binding, short token lifetimes with rotation, and step-up re-authentication on sensitive actions meaningfully constrain a stolen session.

What is refresh-token reuse detection?

When refresh tokens rotate on every use, each token is single-use and its replacement invalidates it. If a token that was already redeemed is presented again, either the client or an attacker holds a stale copy — and in both readings the token family is compromised. The correct response is to revoke the entire family, forcing everyone holding any copy to re-authenticate.

How long should access tokens live?

Short enough that a stolen one expires before it is useful. Fifteen minutes is a practical floor for access tokens on interactive platforms, paired with refresh tokens that rotate on every use and carry device plus IP-range binding. The refresh token, not the access token, is the artifact worth defending, because it is the one with long-lived value.