Skip to content

Enforce absolute maximum session lifetime for web sessions #596

@andrewmusselman

Description

@andrewmusselman

Summary

Web sessions can remain valid indefinitely with continued activity. The application uses sliding session expiration only, with no absolute maximum lifetime enforced. This allows compromised sessions unlimited exploitation windows.

ASVS Requirements

  • 7.3.2 - Verify that there is an absolute maximum session lifetime such that re-authentication is enforced

Related Audit Reports

Affected Files

  • atr/config.py (lines 70-74) - Missing lifetime configuration
  • atr/server.py - Session initialization
  • atr/web.py - Committer class lacks age validation
  • typestubs/asfquart/session.pyi - External library interface

Current Behavior

# atr/config.py lines 70-74
class AppConfig:
    SESSION_COOKIE_SECURE = True
    SESSION_COOKIE_HTTPONLY = True
    SESSION_COOKIE_SAMESITE = "Strict"
    SESSION_COOKIE_NAME = "__Host-session"
    # MISSING: No SESSION_PERMANENT_LIFETIME or absolute timeout

# typestubs/asfquart/session.pyi - confirms sliding expiration only
async def read(expiry_time=..., app=...) -> typing.Optional[ClientSession]:
    """Fetches a cookie-based session if found (and valid), and updates the last access timestamp
    for the session."""
    ...

Risk

  • Sessions with continued activity remain valid indefinitely
  • Compromised sessions cannot be limited by time-based controls
  • Session fixation or hijacking attacks have unlimited exploitation windows
  • Violates compliance requirements (PCI-DSS, SOC2, ASVS)

Recommended Fix

# Option 1: Application-layer enforcement in atr/web.py
from datetime import timedelta, datetime, UTC
from typing import Final

MAX_SESSION_LIFETIME_HOURS: Final[int] = 72

class Committer:
    def __init__(self, web_session: session.ClientSession) -> None:
        self.session = web_session
        self._validate_session_lifetime()

    def _validate_session_lifetime(self) -> None:
        session_created = getattr(self.session, 'created_at', None)
        if session_created is None:
            raise base.ASFQuartException("Session expired", errorcode=401)
        if (datetime.now(UTC) - session_created) > timedelta(hours=MAX_SESSION_LIFETIME_HOURS):
            raise base.ASFQuartException("Session expired", errorcode=401)

# Option 2: Add to atr/config.py
ABSOLUTE_SESSION_MAX_SECONDS: Final[int] = 28800  # 8 hours

Acceptance Criteria

  • Session creation timestamp stored in session metadata
  • Maximum session lifetime configured (recommend 8 hours)
  • Age validation performed on every authenticated request
  • Expired sessions redirected to login with ?reason=session_expired
  • Re-authentication enforced after maximum lifetime exceeded
  • Configuration documented in security documentation

Metadata

Metadata

Assignees

No one assigned

    Labels

    ASVSAnything related to ASVS requirementsL1ASVS L1 requirementcriticalCritical issuesecurityIssues related to security posture

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions