Session timeout enforced server-side with 30-minute idle limit
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:
// 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
expiresInclaim and whether refresh token expiry is reasonable. For server-side sessions, checkmaxAgeorttlsettings. 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:
// 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 — Insufficient Session Expiration
- owasp:2021 · A07 — Identification and Authentication Failures
- nist:rev5 · AC-12 — Session Termination
Taxons
History
- 2026-04-18·v1.0.0·Initial import from security-hardening·automated