# Session timeout enforced server-side with 30-minute idle limit

- **Pattern:** `ab-002377` (`security-hardening.auth-session.session-timeout`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002377
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002377)

## Why it matters

Sessions that never expire or persist for 30 days violate CWE-613 (Insufficient Session Expiration) and OWASP A07 (Identification & Authentication Failures). A session stolen via XSS, network interception, or a shared computer remains valid indefinitely, giving attackers a permanent foothold. NIST 800-53 AC-12 requires session termination after inactivity. For applications touching financial data, health records, or admin functions, indefinite sessions convert a one-time credential theft into permanent account control. Even a 7-day JWT without refresh rotation means a stolen token is valid for a full week after discovery.

## Severity rationale

High because overly long-lived sessions convert temporary access — from a stolen device, XSS attack, or public terminal — into persistent unauthorized control over an account.

## Remediation

Separate short-lived access tokens (15 minutes) from refresh tokens (7 days, rotated on use). Set explicit `maxAge` on session cookies:

```ts
// JWT access token — short-lived
const accessToken = jwt.sign({ userId }, process.env.JWT_SECRET!, { expiresIn: '15m' })

// Session cookie — explicit maxAge in seconds
res.setHeader('Set-Cookie', serialize('session', sessionId, {
  httpOnly: true,
  secure: true,
  sameSite: 'strict',
  maxAge: 30 * 60,  // 30 minutes
}))
```

For admin panels and payment flows, enforce absolute session expiry independent of activity (e.g., 4-hour hard cap even for active sessions).

## Detection

- **ID:** `session-timeout`
- **Severity:** `high`
- **What to look for:** List all session configuration locations and for each, look for session expiry configuration in auth setup. For JWT, check the `expiresIn` claim and whether refresh token expiry is reasonable. For server-side sessions, check `maxAge` or `ttl` settings. Verify that the timeout is enforced server-side (not just a client-side cookie expiry that can be modified). For sensitive applications, idle timeout of ≤30 minutes is expected.
- **Pass criteria:** Sessions or access tokens expire within a reasonable period: access tokens no more than 2 hours, idle session timeout no more than 8 hours for standard apps, and no more than 30 minutes for sensitive operations involving financial data, health data, or admin functions. Expiry is enforced server-side.
- **Fail criteria:** Tokens or sessions never expire, have expiry >7 days without a refresh mechanism, or only rely on client-side cookie expiry without server-side validation.
- **Skip (N/A) when:** The application uses a fully managed auth provider (Clerk, Auth0) that handles session lifecycle externally and you have verified their default timeout settings are appropriate.
- **Detail on fail:** Specify the timeout value. Example: `"JWT tokens issued with expiresIn: '30d' — 30-day tokens are excessively long-lived"` or `"Sessions have no maxAge configured — they persist indefinitely until the user manually logs out"`
- **Remediation:** Set appropriate expiry for tokens and sessions:

  ```ts
  // JWT: short-lived access token + refresh token pattern
  const accessToken = jwt.sign(
    { userId: user.id, role: user.role },
    process.env.JWT_SECRET!,
    { expiresIn: '15m' }  // 15 minutes
  )

  const refreshToken = jwt.sign(
    { userId: user.id, tokenFamily: tokenFamilyId },
    process.env.REFRESH_SECRET!,
    { expiresIn: '7d' }  // 7 days, rotated on use
  )

  // Session cookies: explicit maxAge
  res.setHeader('Set-Cookie', serialize('session', sessionId, {
    httpOnly: true,
    secure: true,
    sameSite: 'strict',
    maxAge: 30 * 60,  // 30 minutes in seconds
    path: '/',
  }))
  ```

  For high-sensitivity operations (admin panels, payment flows), implement absolute session expiry independent of activity.

## External references

- cwe CWE-613
- owasp A07
- nist AC-12

Taxons: access-control

HTML version: https://auditbuffet.com/patterns/ab-002377
