User can view/terminate active sessions remotely
Why it matters
Users who cannot see their active sessions cannot detect unauthorized access. If an attacker establishes a session from an unfamiliar IP or device, the account owner has no mechanism to discover it or revoke it — they must wait for the application to enforce concurrent session limits or for a timeout. CWE-613 and OWASP A07 identify session visibility as a component of proper session management. Giving users a self-service session management page transforms passive victims into active defenders: they can see an unexpected location, terminate the session, and change their credentials before the attacker completes a transaction.
Severity rationale
Low because the attacker still needs a valid session token to exploit the gap, but the absence of session visibility removes the user's only self-service mechanism for detecting and revoking unauthorized concurrent access.
Remediation
Add GET and DELETE endpoints in src/app/api/account/sessions/route.ts:
export async function GET(req: Request) {
const user = await getCurrentUser(req);
const sessions = await db.sessions.findMany({
where: { userId: user.id, expiresAt: { gt: new Date() } },
select: { id: true, device: true, location: true,
loginAt: true, lastActivityAt: true }
});
return Response.json(sessions);
}
export async function DELETE(req: Request) {
const { sessionId } = await req.json();
const user = await getCurrentUser(req);
// Verify ownership before terminating
await db.sessions.updateMany({
where: { id: sessionId, userId: user.id },
data: { terminatedAt: new Date() }
});
return Response.json({ ok: true });
}
The session list must show at least: device description, approximate location, login timestamp, and last-activity timestamp. Gate termination on userId ownership to prevent cross-user session deletion.
Detection
- ID:
user-view-terminate-sessions - Severity:
low - What to look for: Count all API endpoints or UI pages that display active sessions. Enumerate the session data fields shown (device, location, login time, IP). Count all termination endpoints. Verify users can both view and terminate sessions. A view-only display without termination capability does not count as pass.
- Pass criteria: At least 1 API endpoint or UI page shows all active sessions with at least 3 data fields (device, location, login time). At least 1 termination endpoint exists. Report the count even on pass (e.g., "1 GET /api/account/sessions with 4 fields, 1 DELETE endpoint for remote termination").
- Fail criteria: No active sessions view (0 endpoints), or users cannot terminate other sessions remotely (0 termination endpoints).
- Skip (N/A) when: The application enforces single-session-per-user (no other sessions to manage) — cite the actual session limit found.
- Detail on fail:
"0 active session views — users cannot see or manage their sessions"or"Session list exists but 0 termination endpoints — view-only" - Remediation: Add a session management UI and API (in
src/app/api/account/sessions/route.ts):// app/api/account/sessions/route.ts export async function GET(req: Request) { const user = await getCurrentUser(req); const sessions = await db.sessions.findMany({ where: { userId: user.id, expiresAt: { gt: new Date() } }, select: { id, device, location, loginAt, lastActivityAt } }); return Response.json(sessions); } export async function DELETE(req: Request) { const { sessionId } = await req.json(); await db.sessions.update({ where: { id: sessionId }, data: { terminatedAt: new Date() } }); return Response.json({ ok: true }); }
External references
- cwe · CWE-613 — Insufficient Session Expiration
- owasp:2021 · A07
- nist:rev5 · AC-12 — Session Termination
Taxons
History
- 2026-04-18·v1.0.0·Initial import from finserv-session-security·automated