{
    "componentChunkName": "component---src-pages-blog-markdown-remark-fields-slug-js",
    "path": "/blog/session-hijacking-fixation",
    "result": {"data":{"markdownRemark":{"html":"<p>A password protects the front door. A session protects everything behind it. Once a user logs in, the session token becomes the credential for every request that follows, which is exactly why attackers target it. Steal or fix a session and the password, the MFA prompt, and the login form all become irrelevant. This guide lays out a layered defense against the two most common session attacks, session hijacking and session fixation, with concrete controls and notes on where SuperTokens handles the work by default.</p>\n<h2 id=\"what-makes-authentication-sessions-a-top-target-in-2025\" style=\"position:relative;\"><a href=\"#what-makes-authentication-sessions-a-top-target-in-2025\" aria-label=\"what makes authentication sessions a top target in 2025 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>What Makes Authentication Sessions a Top Target in 2025?</h2>\n<p>Sessions carry post-login trust. After authentication succeeds, the server issues a token that represents “this request comes from an authenticated user,” and every subsequent request rides on that token instead of re-checking the password. If an attacker steals or fixes a session ID, they inherit that trust and bypass passwords and MFA entirely.</p>\n<p>This is not a theoretical concern. Token theft has become a primary attack vector precisely because a stolen token is a valid token: it sails past SSO, MFA, and conditional access policies that only fire at login time. OWASP has long flagged strict session timeouts and robust session management as core defenses, because the session layer is where authentication actually lives once the login flow is over.</p>\n<p>The rest of this guide treats session security as a layered problem: no single control is sufficient, and even if one layer fails, the others contain the damage.</p>\n<h2 id=\"threat-model-101-hijacking-vs-fixation\" style=\"position:relative;\"><a href=\"#threat-model-101-hijacking-vs-fixation\" aria-label=\"threat model 101 hijacking vs fixation permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Threat Model 101: Hijacking vs. Fixation</h2>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/1162c33d11825bfa5dd1f688f45e7434/71c1d/Hijacking-vs-Fixation.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 66.45569620253164%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC60lEQVQ4yyWTy4sjVRjFb1XqXXXvrXfqkU4lnbQzSXfS73SnX/MQcWAEBcERRUYQF6LgQty6mp3gzqVb/82fVrL44N5vcfg4v3OEEAH7cfGjgsvbJ06v7lhdbLnYPHJ2ecdqveHh4TXX13ecnd/y7Nmaq6st9/cvqZtDhPARRrSfXqx2YoaWQluSw3TENO+YpAfUcUOdjtC6oqgXtLMndHZIXC5QxRJdHmP5IwZOiWlnCEMiPDPim2rGi3zMgaNZ+xnPvYTTqGDqaiozxBMuyfg1y9v3GG6LcGdYeo2lVxjBEUKkGN4BhpUiEktxEOYMVclT0uKZAdLw+evvf/nu25+QwkK6CX52QVHNeH684dUnXyLMDOGNmXx0w4c//yFvFgihEN5AEtqS0JKM/RTPT9BRwfb+DfPZmsgKCW2N6eQIERKqlpPTe3Q+w/RqonTK1fYNnmwRgwSRmJLNaE7RHXGWtnycjRGGTyBsGi/GGoQ4pkQ2Lzhe3xIPlwh9jRNUCDOnPDjlq/e/I8v13kPbjHBsuSPcw+hUhWGGOEaALRwGIsC1FJZbEukGK+wQ3gJfjfBUR1Yv+eLrXwmL871gNlDctHPa+ZLzZsZF3PToefX4lh9/+wMjULsLo/qRpluRDydsHz5F5/Odj3l7wufvfiHKFwhDIRwzQnoxQZTTyZKz4RTlp1yebPns3fcMZIJnSmy/JVAtMhlzc/+WujvDkx3t9IIffv6AqjcIEe2hDC2JNgICM9wRn6uKSPhYwqAYSKStsfw+LiPEIEdYLW66xk+P9/9eyG0x7QLhDiTL4Zg2qShkQRmmZGHGNB+h/YRpXBH0SYgP0dUKJxhSdBucsEYWC3w9IcqOUMMTDCtD9IZXaUMR1+ggQ8sRSVSSyBLppcRRQeik+GqMTKf4qiPOZ3hyTJRMdvtAdYS6w+jbsuufqTHsdJc1LztH5XNkOtkJRMmUQI/xVYsvex9HuFGze+8E+nb0l/VA/tf6D3qrSs+5HtemAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Hijacking-vs-Fixation\"\n        title=\"Hijacking-vs-Fixation\"\n        src=\"/static/1162c33d11825bfa5dd1f688f45e7434/f058b/Hijacking-vs-Fixation.png\"\n        srcset=\"/static/1162c33d11825bfa5dd1f688f45e7434/c26ae/Hijacking-vs-Fixation.png 158w,\n/static/1162c33d11825bfa5dd1f688f45e7434/6bdcf/Hijacking-vs-Fixation.png 315w,\n/static/1162c33d11825bfa5dd1f688f45e7434/f058b/Hijacking-vs-Fixation.png 630w,\n/static/1162c33d11825bfa5dd1f688f45e7434/40601/Hijacking-vs-Fixation.png 945w,\n/static/1162c33d11825bfa5dd1f688f45e7434/78612/Hijacking-vs-Fixation.png 1260w,\n/static/1162c33d11825bfa5dd1f688f45e7434/71c1d/Hijacking-vs-Fixation.png 1536w\"\n        sizes=\"(max-width: 630px) 100vw, 630px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<p>The two attacks sound similar but work in opposite directions, and they need different defenses.</p>\n<ul>\n<li><strong>Session hijacking</strong> is theft of a valid session token from a legitimate user. The attacker obtains a token that was correctly issued to someone else, through cross-site scripting (XSS) that reads the token, malware on the device, network sniffing over unencrypted HTTP, or direct device compromise. The defenses limit both the chance of a leak and its usefulness if one happens: HTTPS with HSTS, <code class=\"language-text\">HttpOnly / Secure / SameSite</code> cookies, idle and absolute timeouts, and token rotation.</li>\n<li><strong>Session fixation</strong> runs the other way. The attacker sets a known session ID <em>before</em> the victim logs in, then waits. If the application reuses that same session ID after authentication instead of generating a fresh one, the attacker already knows the now-authenticated session ID and can reuse it. The single most important defense is to regenerate the session ID on login and at any privilege change, so any pre-login identifier becomes worthless the moment the user authenticates.</li>\n</ul>\n<h2 id=\"baseline-controls-apply-everywhere\" style=\"position:relative;\"><a href=\"#baseline-controls-apply-everywhere\" aria-label=\"baseline controls apply everywhere permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Baseline Controls (Apply Everywhere)</h2>\n<p>These are the non-negotiables. Every application handling sessions should have all of them in place regardless of framework or platform.</p>\n<ul>\n<li><strong>HTTPS everywhere plus HSTS.</strong> Encrypt every request so tokens cannot be sniffed in transit, and use HSTS to prevent downgrade to plain HTTP.</li>\n<li><strong>Cookie flags.</strong> Set <code class=\"language-text\">HttpOnly</code> so JavaScript cannot read the token, <code class=\"language-text\">Secure</code> so it only travels over HTTPS, and <code class=\"language-text\">SameSite</code> to cut off cross-site request paths.</li>\n<li><strong>Rotate the session ID on login.</strong> This is the fixation defense. Regenerate on authentication and again on any privilege elevation.</li>\n<li><strong>Timeouts.</strong> Enforce both an idle timeout and an absolute lifetime, and keep access tokens short-lived.</li>\n<li><strong>No session IDs in URLs.</strong> Never place a token in a query string, and never accept a session ID supplied through <code class=\"language-text\">GET</code> or <code class=\"language-text\">POST</code> parameters. A session ID in a URL leaks through browser history, server logs, referer headers, and shared links.</li>\n</ul>\n<h2 id=\"implementation-steps-secure-cookies-and-csrf-posture\" style=\"position:relative;\"><a href=\"#implementation-steps-secure-cookies-and-csrf-posture\" aria-label=\"implementation steps secure cookies and csrf posture permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Implementation Steps: Secure Cookies and CSRF Posture</h2>\n<p>Start by mapping every cookie the application sets: its name, purpose, and scope (domain and path). An inventory is the only way to know what needs protecting.</p>\n<p>Then set the flags. For authentication cookies, <code class=\"language-text\">HttpOnly</code> and <code class=\"language-text\">Secure</code> are mandatory. For <code class=\"language-text\">SameSite</code>, prefer <code class=\"language-text\">Lax</code> or <code class=\"language-text\">Strict</code> wherever the application’s flows allow it. Some legitimate cross-site flows require <code class=\"language-text\">SameSite=None</code>, and when they do, that cookie must also carry <code class=\"language-text\">Secure</code>. Document every place a cross-site flow forces <code class=\"language-text\">SameSite=None</code> so the exception is deliberate rather than accidental.</p>\n<p>CSRF strategy follows from the cookie posture. Two approaches work and combine well: anti-CSRF tokens the server verifies on state-changing requests so a cross-site request cannot forge them, and <code class=\"language-text\">SameSite</code>cookies backed by server-side <code class=\"language-text\">Origin</code> or <code class=\"language-text\">Referer</code> verification on any request that changes state. The important rule is that state-changing requests (anything that writes, deletes, or transfers) must be verified server-side. Read-only requests carry less risk, but any mutation needs an explicit CSRF defense.</p>\n<h2 id=\"rotation-and-revocation-stop-replay-and-long-lived-risk\" style=\"position:relative;\"><a href=\"#rotation-and-revocation-stop-replay-and-long-lived-risk\" aria-label=\"rotation and revocation stop replay and long lived risk permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Rotation and Revocation: Stop Replay and Long-Lived Risk</h2>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/51c9cdbec24b1e254cb260ec4a044c3f/71c1d/Rotation-and-Revocation.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 66.45569620253164%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAACQElEQVQoz02RyY7cOAyGtVtW2ZZEyVq8l6tr7+oknWAukwCZy1yCIMHM+79L4M4lwHchwZ/8SSJEtfZTmw4hrz7u27R2w+N2/3m//3fcfzsdvz8//389/cj9iw2zTQc/nEN/8unQwIgQbXxau+myv3+5fvg67B/ddO3ny3i45Zfr9Lg9vb5b378sl0fojo/9/e/r68fz+366QFwR5g4RjdCOwCs3Z8wcEZZQjVjFLxlHi7wmEXDZIFQduD0JE0iN0Q7hGmHVlXaVu0DKFu+yhEXnc+kWWo1U53JNcoqIG8wcLhxiFm19LeJAhENcgFJBqMBKx5SjJZQ60RI2apCuLWy7eeEbO25TFY0EzDTmgJSABGMNo6CGU8PYG/QNYjAxGGvG7O/kpPxfaT43kVGNOCCpdEx96E6bmT+htij1Mro+Wqh8bhenu8ztWbpJAGVmm8ykK6uomu4tsEQAKQDzTSyVHYe+y1nIRpsupFlKkIXj3OLfYlT2Ol7ycMRFIEV0YR/yk1AZF4nsFpk+cHdDzCFqEDOI6e01283exEJ2IV5tuyBiZDlYv6/MgCkgaqScD/2/Q/uZsoiZpTzUevZpNX5G3G5iKrxQkZee8IYXUOmkmoQFYGGJbNVukWokpafCUQ5KtnUVmzqJrQAQwvY815/f2WXOfR/btpZciefB/PMEt32zds2SSQQl7Xc7f/LTw8996bMAtu1MrbN67CDEIeUBwIuiweCKIdp+Nv1kh5mDF8IeVeh037spNtkWnnD7C00TNjnC/Jz7AAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Rotation-and-Revocation\"\n        title=\"Rotation-and-Revocation\"\n        src=\"/static/51c9cdbec24b1e254cb260ec4a044c3f/f058b/Rotation-and-Revocation.png\"\n        srcset=\"/static/51c9cdbec24b1e254cb260ec4a044c3f/c26ae/Rotation-and-Revocation.png 158w,\n/static/51c9cdbec24b1e254cb260ec4a044c3f/6bdcf/Rotation-and-Revocation.png 315w,\n/static/51c9cdbec24b1e254cb260ec4a044c3f/f058b/Rotation-and-Revocation.png 630w,\n/static/51c9cdbec24b1e254cb260ec4a044c3f/40601/Rotation-and-Revocation.png 945w,\n/static/51c9cdbec24b1e254cb260ec4a044c3f/78612/Rotation-and-Revocation.png 1260w,\n/static/51c9cdbec24b1e254cb260ec4a044c3f/71c1d/Rotation-and-Revocation.png 1536w\"\n        sizes=\"(max-width: 630px) 100vw, 630px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<p>Short-lived access tokens limit the blast radius of a leak. An access token valid for minutes rather than days expires before a stolen one can do much damage. Pair short access tokens with rotating refresh tokens so the long-lived credential is never static.</p>\n<p>Rotation is where theft becomes detectable. Every time a refresh token is used, it is replaced and the old one is invalidated. If a rotated (already-used) refresh token shows up again, that is strong evidence of theft: either the attacker or the legitimate user is presenting a token that was already spent. The correct response is to assume compromise and revoke the entire session family, forcing both parties to re-authenticate. Rotation converts silent compromise into a detectable event: without it, a stolen refresh token is a permanent key; with it, the theft announces itself the moment both parties try to refresh.</p>\n<p>Beyond the automatic case, revocation should be wired to risk signals. Force re-authentication and revoke sessions on password reset, on role or privilege changes, and on device-risk signals. A password reset that leaves old sessions alive is a common and dangerous gap, since it leaves an attacker with continued access even after the user thinks they have locked things down.</p>\n<h2 id=\"detection-signals-when-a-session-might-be-stolen\" style=\"position:relative;\"><a href=\"#detection-signals-when-a-session-might-be-stolen\" aria-label=\"detection signals when a session might be stolen permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Detection Signals: When a Session Might Be Stolen</h2>\n<p>Some signals are strong enough to act on automatically, others are worth surfacing for review.</p>\n<ul>\n<li><strong>Refresh token reuse or mismatch.</strong> With rotation in place, a reused refresh token is the strongest available theft indicator, and it justifies automatic revocation.</li>\n<li><strong>Improbable travel or device change.</strong> A session that jumps continents in minutes, or moves to an unrecognized device, deserves a step-up challenge. Treat IP change as an anomaly signal rather than a hard block, since VPNs and mobile networks change IPs legitimately.</li>\n<li><strong>Anomalous API usage.</strong> Sudden shifts in request rate, or access to scopes the user never touches, can indicate a hijacked session.</li>\n</ul>\n<p>SuperTokens surfaces token theft directly. When rotation detects a refresh token mismatch, it raises a theft-detected condition that application code can act on: revoke the session, notify the user, or trigger a risk challenge. The detection is built into the session layer rather than something teams assemble themselves.</p>\n<h2 id=\"fixation-specific-hardening-at-login-and-privilege-jumps\" style=\"position:relative;\"><a href=\"#fixation-specific-hardening-at-login-and-privilege-jumps\" aria-label=\"fixation specific hardening at login and privilege jumps permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Fixation-Specific Hardening (at Login and Privilege Jumps)</h2>\n<p>Fixation has a narrow, reliable fix, and the whole battle is making sure it happens at the right moments.</p>\n<p>Always regenerate the session identifier on successful authentication and on any privilege elevation, such as a user stepping into an admin context. Any session ID that existed before the login boundary must not survive it.</p>\n<p>Pre-authentication session state needs care. Data like a shopping cart or CSRF state attached to the pre-login session should be dropped and re-bound to the freshly issued session, rather than carried over on the old identifier, since carrying state across on the old ID is exactly what fixation exploits.</p>\n<p>Finally, block attacker-controlled identifiers at the door. Never accept a session token or ID from a URL, a form field, or a JSON body. The server issues session identifiers; it does not accept them from the client.</p>\n<h2 id=\"monitoring-and-timeouts-that-actually-work\" style=\"position:relative;\"><a href=\"#monitoring-and-timeouts-that-actually-work\" aria-label=\"monitoring and timeouts that actually work permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Monitoring and Timeouts That Actually Work</h2>\n<p>Timeouts are a blunt but effective containment tool, and they work best in combination.</p>\n<ul>\n<li><strong>Idle timeout.</strong> Expire a session after a period of inactivity, tuned to the sensitivity of the application. A banking app warrants a tighter idle window than a note-taking app.</li>\n<li><strong>Absolute timeout.</strong> Cap the total lifespan of a session regardless of activity, so no session lives forever even if the user stays active.</li>\n<li><strong>Shorter lifetimes on sensitive paths.</strong> Admin and other high-privilege areas should carry shorter session lifetimes and force step-up authentication before granting access.</li>\n</ul>\n<p>The idle timeout catches abandoned sessions like an unlocked laptop, while the absolute timeout caps the reward for a token that was stolen and kept active. Both are needed.</p>\n<h2 id=\"how-supertokens-hardens-session-security\" style=\"position:relative;\"><a href=\"#how-supertokens-hardens-session-security\" aria-label=\"how supertokens hardens session security permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>How SuperTokens Hardens Session Security</h2>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/9e2ba8940c8f1123465e76174989af40/d0c2f/Supertokens.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 47.46835443037975%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsTAAALEwEAmpwYAAABqElEQVQoz02PbW/aMBSFMy0kvo6d2InjkDgJBEJJeX8tpGzVoIxO2mhFK6H9LH7yBJGqPTrf7nmke7RR5n8r5Pdxp+x3eqHXj8Q0U+MsnOTJXRK24jAQnu8L3xdSSiGE53lFUVwul81mow1iPlRsrthK8bVy18qdKa/sNleDYpGn4+Iuz3Pf9wHwJwCg6zoAaJ3AHtbtpWKl4quQz+p81lBP8+nDbLaZjlaLxXA8aWYZIQT+w7qhxS7pSrsv7YGgA+G0GZnk7fPp/eP942m7/3N8fTm87LbbKKzr+leEACFU+QghzaVW5JCUWS1mtV163G1/7/fPP3avb6fz+e/pdHo7Hn8+734dDo9liTE2b1BKGGOahcGxsCA4oDi0rbaK+t1u7763XC4f16vyYb6YjsbDXt5qSF+gG7VaTcowa91rAIABCCAHg4OBAhIuD4MgjaMsVa0kakQyFJzbxDSMSjYMw+NuohpXGd0GQBUAauiSs0YcN5NrsjRhGByzVtUQArOmUz+V2UD7XF9hXo/G9RFicZtyhzJqWcgE0zCrBoCpf6HJKBge/gGUPjjwL5U6kAAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Supertokens\"\n        title=\"Supertokens\"\n        src=\"/static/9e2ba8940c8f1123465e76174989af40/f058b/Supertokens.png\"\n        srcset=\"/static/9e2ba8940c8f1123465e76174989af40/c26ae/Supertokens.png 158w,\n/static/9e2ba8940c8f1123465e76174989af40/6bdcf/Supertokens.png 315w,\n/static/9e2ba8940c8f1123465e76174989af40/f058b/Supertokens.png 630w,\n/static/9e2ba8940c8f1123465e76174989af40/40601/Supertokens.png 945w,\n/static/9e2ba8940c8f1123465e76174989af40/78612/Supertokens.png 1260w,\n/static/9e2ba8940c8f1123465e76174989af40/d0c2f/Supertokens.png 1362w\"\n        sizes=\"(max-width: 630px) 100vw, 630px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<p><a href=\"https://supertokens.com/\" target=\"_blank\" rel=\"nofollow\">SuperTokens</a> builds most of these controls into the session layer by default. The features map to the defenses above as follows.</p>\n<ul>\n<li><strong>Cookie security and CSRF.</strong> The web SDKs default to secure cookie settings for browser apps, storing tokens in <code class=\"language-text\">HttpOnly</code> cookies so JavaScript cannot read them. Anti-CSRF handling is integrated into the session layer alongside <code class=\"language-text\">SameSite</code> configuration.</li>\n<li><strong>Rotating refresh tokens with theft detection.</strong> This is the core differentiator. The server issues rotating refresh tokens and detects reuse or mismatch. When a rotated token is presented again, SuperTokens raises a theft-detected condition, and the session family can be revoked so both the attacker and the legitimate user are forced to re-authenticate.</li>\n<li><strong>Fixation defense by design.</strong> New session tokens are issued on sign-in, and the guidance is to rotate tokens after authentication, which neutralizes fixation because any pre-authentication identifier is discarded at login.</li>\n<li><strong>Short-lived access tokens with automatic refresh.</strong> Access tokens are short-lived and refreshed transparently by the SDK, limiting the blast radius if one leaks while keeping the user logged in without friction.</li>\n<li><strong>Portable verification via JWKS.</strong> SuperTokens exposes a first-party JWKS endpoint at <code class=\"language-text\">&lt;YOUR_API_DOMAIN>/auth/jwt/jwks.json</code>, so any service can verify access tokens locally using the public keys, with automatic key rotation handled centrally.</li>\n</ul>\n<h2 id=\"copypaste-playbooks-by-platform\" style=\"position:relative;\"><a href=\"#copypaste-playbooks-by-platform\" aria-label=\"copypaste playbooks by platform permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Copy/Paste Playbooks (by Platform)</h2>\n<p>The right controls shift slightly depending on where the session lives.</p>\n<ul>\n<li><strong>Web SPA plus API.</strong> Store the session in <code class=\"language-text\">HttpOnly</code>, <code class=\"language-text\">Secure</code>, <code class=\"language-text\">SameSite</code> cookies, never in <code class=\"language-text\">localStorage</code>, which is readable by any script and therefore exposed to XSS. Enable anti-CSRF plus origin checks on state-changing routes. Configure the SuperTokens web and backend SDKs, and confirm the cookie domain and path match the application’s topology.</li>\n<li><strong>Mobile plus API.</strong> Use the platform’s secure storage for tokens, pin TLS in sensitive contexts, and prefer short access tokens paired with rotating refresh tokens. Mobile networks change IPs constantly, so lean on device binding rather than IP as the primary signal.</li>\n<li><strong>Multi-service backends.</strong> Verify JWTs locally from the JWKS endpoint rather than calling a central authentication service on every request. Set absolute lifetimes and wire centralized revocation to risk signals so a compromised session can be killed across every service at once.</li>\n</ul>\n<p>A minimal Express route using the SuperTokens <code class=\"language-text\">verifySession</code> middleware:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"17184033215716354000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied!\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`import express from &quot;express&quot;;\nimport { verifySession } from &quot;supertokens-node/recipe/session/framework/express&quot;;\n\nconst app = express();\n\napp.post(&quot;/transfer&quot;, verifySession(), async (req, res) => {\n  const userId = req.session.getUserId();\n  // Session is verified. Proceed with the state-changing action.\n  res.json({ status: &quot;ok&quot;, userId });\n});`, `17184033215716354000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                <svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> express <span class=\"token keyword\">from</span> <span class=\"token string\">\"express\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> verifySession <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">\"supertokens-node/recipe/session/framework/express\"</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> app <span class=\"token operator\">=</span> <span class=\"token function\">express</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\napp<span class=\"token punctuation\">.</span><span class=\"token function\">post</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/transfer\"</span><span class=\"token punctuation\">,</span> <span class=\"token function\">verifySession</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">req<span class=\"token punctuation\">,</span> res</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> userId <span class=\"token operator\">=</span> req<span class=\"token punctuation\">.</span>session<span class=\"token punctuation\">.</span><span class=\"token function\">getUserId</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// Session is verified. Proceed with the state-changing action.</span>\n  res<span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">status</span><span class=\"token operator\">:</span> <span class=\"token string\">\"ok\"</span><span class=\"token punctuation\">,</span> userId <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>For services that verify tokens without the backend SDK, point a standard JWKS client at the SuperTokens endpoint:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"4909982199243523000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied!\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`import jwksClient from &quot;jwks-rsa&quot;;\n\nconst client = jwksClient({\n  jwksUri: &quot;<YOUR_API_DOMAIN>/auth/jwt/jwks.json&quot;,\n});\n// Use the fetched signing key with any standard JWT library to verify the token.`, `4909982199243523000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                <svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> jwksClient <span class=\"token keyword\">from</span> <span class=\"token string\">\"jwks-rsa\"</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> client <span class=\"token operator\">=</span> <span class=\"token function\">jwksClient</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">jwksUri</span><span class=\"token operator\">:</span> <span class=\"token string\">\"&lt;YOUR_API_DOMAIN>/auth/jwt/jwks.json\"</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">// Use the fetched signing key with any standard JWT library to verify the token.</span></code></pre></div>\n<h2 id=\"red-team-tests-to-add-to-ci\" style=\"position:relative;\"><a href=\"#red-team-tests-to-add-to-ci\" aria-label=\"red team tests to add to ci permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Red-Team Tests to Add to CI</h2>\n<p>Session defenses rot quietly. A refactor can drop a cookie flag or skip the rotation step, and nothing visibly breaks until an attacker finds it. Encode the defenses as tests so a regression fails the build.</p>\n<ul>\n<li><strong>Fixation test.</strong> Capture the session ID before login and after login, and assert they differ. If the ID survives authentication, fixation is possible.</li>\n<li><strong>Cookie flags test.</strong> Assert that authentication cookies carry <code class=\"language-text\">HttpOnly</code>, <code class=\"language-text\">Secure</code>, and the intended <code class=\"language-text\">SameSite</code> value.</li>\n<li><strong>Rotation test.</strong> Simulate refresh token reuse by presenting an already-used refresh token, and expect theft detection to fire and the session to be revoked.</li>\n<li><strong>Timeout test.</strong> Confirm that both idle and absolute expiry behave as configured, including that an expired token is actually rejected.</li>\n</ul>\n<p>These four cover the highest-value failure modes and run fast enough to sit in CI on every commit.</p>\n<h2 id=\"executive-checklist-fast-review\" style=\"position:relative;\"><a href=\"#executive-checklist-fast-review\" aria-label=\"executive checklist fast review permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Executive Checklist (Fast Review)</h2>\n<p>Four questions cover the essentials:</p>\n<ul>\n<li>Are HTTPS plus HSTS, cookie flags, and CSRF controls enforced across the application?</li>\n<li>Does the session ID rotate on login and on any role or privilege change?</li>\n<li>Are access tokens short-lived, with refresh rotation and theft detection in place?</li>\n<li>Are both idle and absolute timeouts defined, enforced, and monitored?</li>\n</ul>\n<p>Any “no” is the place to start.</p>\n<h2 id=\"supertokens-configuration-checklist-fast-reference\" style=\"position:relative;\"><a href=\"#supertokens-configuration-checklist-fast-reference\" aria-label=\"supertokens configuration checklist fast reference permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>SuperTokens Configuration Checklist (Fast Reference)</h2>\n<p>For teams using SuperTokens, the concrete configuration points are:</p>\n<ul>\n<li><strong>Session recipe.</strong> Enable rotating refresh tokens with theft detection, and handle the theft-detected path by revoking the session and prompting re-authentication.</li>\n<li><strong>Cookies.</strong> Set <code class=\"language-text\">HttpOnly</code>, <code class=\"language-text\">Secure</code>, and the appropriate <code class=\"language-text\">SameSite</code> value for the domain topology.</li>\n<li><strong>Post-login rotation.</strong> Confirm tokens are re-issued on sign-in and at step-up events.</li>\n<li><strong>JWKS verification.</strong> Point services at <code class=\"language-text\">&lt;YOUR_API_DOMAIN>/auth/jwt/jwks.json</code> for local JWT verification.</li>\n</ul>\n<h2 id=\"conclusion\" style=\"position:relative;\"><a href=\"#conclusion\" aria-label=\"conclusion permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Conclusion</h2>\n<p>Secure authentication sessions are not the product of a single setting. They come from layered controls: rotate on login to stop session fixation, guard cookies with <code class=\"language-text\">HttpOnly / Secure / SameSite</code> and close the CSRF paths, enforce idle and absolute timeouts, and pair short-lived access tokens with rotating refresh tokens and theft detection. Each layer covers a different failure, and together they contain the damage when one slips.</p>\n<p>SuperTokens operationalizes these practices with built-in rotation, token theft detection, secure cookie defaults, and a first-party JWKS endpoint for scalable verification, so a team can ship a hardened session layer without hand-building each control or trading away security to move quickly.</p>","frontmatter":{"date":"July 08, 2026","title":"Mastering Session Security: SuperTokens’ Attack Mitigation","cover":"session-hijacking-fixation.png","author":"Mostafa Ibrahim","description":"Stop session hijacking and fixation with a layered plan: timeouts, cookie flags, rotation, and SuperTokens’ theft detection for secure authentication sessions."},"fields":{"slug":"/session-hijacking-fixation/"}},"site":{"siteMetadata":{"title":"SuperTokens Blog"}}},"pageContext":{"id":"bd608211-9d21-5b66-99a3-d9e92951acd9","fields__slug":"/session-hijacking-fixation/","__params":{"fields__slug":"session-hijacking-fixation"}}},
    "staticQueryHashes": []}