Skip to content

TOTP-Based Session Cookies to defend against session hijacking #673

@abose

Description

@abose

Hi all,

I created a proposal to secure sessions with TOTPs. The proposal was discussed in WICG/proposals#204 but i don't know if that was the right place to discuss that topic. If this is not the correct please close the issue.

I have added a brief note below of the general idea below, and I'd love your thoughts on the same if this is any good:

Introduction

Web sessions typically rely on bearer tokens (cookies, JWT) that, once stolen, can be replayed indefinitely until they expire. Even short-lived cookies can be a risk if stolen. While 2FA/TOTP is commonplace at login time, there is no built-in browser support for automatically rotating per-request tokens using TOTP. This leaves session cookies vulnerable to:

  • Session hijacking (cookie theft & reuse)
  • Replay attacks in the token’s valid window

Proposed Solution

This proposal is for a new browser-managed TOTP storage and associated HTTP headers so that:

  1. A server can securely provision a TOTP seed to the browser (via a special header).
  2. The browser automatically rotates and injects a TOTP code with every request (similar to cookies).
  3. This code is time-bound (e.g., 30-second window), drastically reducing replay risk.
  4. The seed is stored securely(Eg. OS Key-chain), inaccessible to JavaScript (like HttpOnly cookies).

By doing this at the browser layer, we eliminate the need for applications to handle or expose TOTP seeds in JavaScript, reduce the chance of XSS-based theft, and create a more secure session mechanism.


2. Use Cases

  1. Enhanced Session Security: Websites that want a short-lived, continuously rotating token. If an attacker steals a TOTP code, it becomes invalid within ~30 seconds.
  2. Sensitive Operations: For YouTube sessions, banking, enterprise dashboards, or admin interfaces, continuous rotation reduces the attack window of a stolen cookie.

3. Proposed HTTP Headers & Behavior

3.1 Set-TOTP-Seed Response Header

A server sets a TOTP seed via an HTTP response header:

Set-TOTP-Seed: base32encodedseed; interval=30; digits=6; scope=session
  • base32encodedseed: The TOTP seed per [RFC 6238 / RFC 4226].
  • interval: How many seconds each code is valid (default: 30).
  • digits: How many digits in each code (default: 6).
  • scope: Defines lifetime or domain rules (e.g., session, persistent, Domain=example.com, etc.).
  • Optional parameters could include algorithm (SHA-1, SHA-256), start, or expires.

Storage

  • The browser MUST store this seed in a secure, origin-bound location, not accessible to JavaScript (similar to HttpOnly cookies or [WebAuthn] credential storage).
  • If the site is https://example.com, the TOTP seed is only used for requests to example.com over HTTPS.
  • Storing TOTP seed in system keychain will improve security posture.

3.2 Automatic TOTP Injection in Requests

For subsequent HTTP requests to the same origin:

  1. The browser generates a fresh TOTP code (e.g., every 30s).
  2. It appends a header (or cookie) to the request:
    TOTP-Code: 123456
    
  3. The server validates the code server-side (matching the seed and time window).

Example Flow

  1. Initial Login:

    HTTP/1.1 200 OK
    Set-Cookie: sessionid=abc123; HttpOnly; Secure; SameSite=Strict
    Set-TOTP-Seed: JBSWY3DPEHPK3PXP; interval=30; digits=6; scope=session
    Content-Type: text/html
    
    <html> ... </html>
  2. Subsequent Request:

    GET /account
    TOTP-Code: 654321
    Cookie: sessionid=abc123

3.3 Rotation & Revocation

  • Rotation: A server might issue a new TOTP seed at certain intervals or after suspicious activity:
    Set-TOTP-Seed: newSeedValue; ...
    
    The browser updates storage accordingly.
  • Revocation: The server can invalidate the old seed on the server side. If the user reuses an older TOTP
    code, the server rejects it.

4. No JavaScript API

The default flow requires no JS API usage. This is safer.


5. Security Notes

  1. Seed Storage: Must be at least as secure as HttpOnly cookies. Ideally, it should leverage OS-level credentials storage (OS keychain or like [WebAuthn] private keys do).
  2. Replay Window: A TOTP code is valid for the specified time interval. Attackers who intercept traffic in real time could use a code within that short window.
  3. Mixed Content: Must only work on secure contexts (https://) to prevent network tampering.
  4. Phishing: Attackers could still prompt users to visit malicious pages. However, TOTP codes rotate quickly. Proper domain checks minimize risk.
  5. XSS: Because seeds are never exposed to JS, XSS can’t read or replicate them.

6. Privacy Notes

  • Fingerprinting: TOTP codes are ephemeral and supplement standard cookies, so minimal new fingerprinting surface(same surface as cookies).
  • Cross-domain: Seeds must not be shared across domains unless explicitly allowed (like cookies with a Domain= attribute).
  • User Awareness: Typically invisible to end-users, but must comply with existing privacy/cookie consent
    requirements.

7. Alternatives & Prior Art

  • Application-layer TOTP: Storing seeds in localStorage, generating codes in JS.
    • > Susceptible to XSS or malicious scripts.
  • Short-lived JWT + Refresh tokens: De facto standard in many SPAs, but still can be stolen if not protected
    properly. Refresh tokens are still long lived. Even with HttpOnly cookies, a stolen refresh token (e.g., via malware) can be replayed until it’s revoked or expired, which brings us back to your original problem.
  • WebAuthn: Solves passwordless or 2FA login. Does not sign each request or handle session rotation.
  • mTLS (Mutual TLS): Binds sessions to a client certificate. Strong security but complicated user flows.

This proposal focuses on a simpler, ubiquitous TOTP approach that is time-bound and easy to implement server-side.


8. Compatibility & Migration with Existing Session Cookies

This proposal is incremental and aims to work alongside existing session cookie flows, preserving backward compatibility. Specifically:

  • Progressive Enhancement
    • Browsers that support TOTP-based session headers can respond with TOTP-Code automatically for each request.
    • On the server side, if TOTP-Code is present, the server validates it in addition to the existing session cookie.
  • Fallback for Non-Supporting Browsers
    • If a browser does not support TOTP-based session headers, it won’t send TOTP-Code.
    • The server can detect its absence and fall back to traditional session validation (e.g., cookies or JWT).
  • Incremental Rollout
    • During a migration window, the server can be configured to:
      1. Send Set-TOTP-Seed to supporting browsers.
      2. Accept either (session cookie + TOTP-Code) or just (session cookie) if TOTP is not present.
    • Once you confirm that the user’s browser supports TOTP by seeing a valid TOTP-Code, the server can bind the session to TOTP-based tokens going forward.
  • No Breaking Changes
    • Existing logins, cookies, and session flows continue working for older browsers or for use cases where TOTP-based rotation isn’t needed.

This approach avoids forcing a hard cutover. It lets websites gracefully adopt TOTP-based session rotation while supporting a range of clients and existing infrastructure.

9. Open Questions

  1. Exact Header Names: TOTP-Code vs. Sec-TOTP or a cookie approach.
  2. Multiple Seeds: If a user has multiple seeds or intervals, how is that managed?
  3. Algorithm Customization: Should we allow specifying SHA256 or just default to SHA1 for TOTP?

10. Conclusion

This proposal outlines a browser-native TOTP mechanism for short-lived, rotating session tokens. It aims to reduce session hijacking risks by limiting replay windows without the overhead or complexity of fully custom cryptographic flows in application code.


11. References


12. Feedback

This document is a draft for discussion.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions