Inspect JWT lifetime
- Issued token [REDACTED-LAB-TOKEN]
- Decoded payload { "user_id": 2, "username": "pentest_attacker", "is_admin": false, "iat": 1790737980 }
- Claims present iat, is_admin, user_id, username
- Expiry claim (exp) absent
LAB FAIL
Insufficient Session Expiration — Authentication — JWT Lifetime
CWE-613: Insufficient Session Expiration · OWASP A07:2021 - Identification and Authentication Failures
[FAIL] Insufficient Session Expiration - Authentication - JWT Lifetime
Issued JWT tokens contain no exp claim, so they never expire, and there is no token revocation mechanism. The token is also accepted from several locations at once, including URL query parameters, and in the web interface it is stored in localStorage where JavaScript can read it.
Medium: a token that leaks once remains valid forever. There is no rotation or revocation, so a single leak grants permanent access to the victim's account. Accepting tokens via query parameters increases the chance of leakage because URLs are recorded in browser history, server logs, and the Referer header. Storing the token in localStorage makes it stealable via XSS, which was proven in the Stored Cross-Site Scripting finding where an administrator JWT was successfully captured.
CWE-613: Insufficient Session Expiration
A07:2021 - Identification and Authentication Failures
Token analysis: an issued token is decoded and contains only the user_id, username, is_admin, and iat claims; there is no exp claim at all. Verification: the same token is still accepted by a protected endpoint. Supporting evidence: the token is also accepted when sent as a URL query parameter, not only in the Authorization header.
POST http://127.0.0.1:5001/login (token issuance) GET http://127.0.0.1:5001/dashboard (token acceptance)
JWT-based session management
- Add a short-lived exp claim and implement revocable refresh tokens. - Maintain a revocation list (denylist) or use token identifiers so sessions can be terminated during an incident. - Accept tokens only from the Authorization header; stop reading them from query parameters and forms. - Move session tokens into HttpOnly, Secure, and SameSite cookies so JavaScript cannot reach them.
LAB All targets are lab systems. Nothing here is a client engagement. Evidence is shown as recorded; secrets are blacked out.
GET /dashboard HTTP/1.1
User-Agent: python-requests/2.33.1
Accept-Encoding: gzip, deflate, br, zstd
Accept: */*
Connection: keep-alive
Authorization: Bearer [REDACTED-LAB-TOKEN]
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 27266
Access-Control-Allow-Origin: *
Server: Werkzeug/2.0.1 Python/3.9.25
Date: Wed, 30 Sep 2026 03:13:01 GMT
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Dashboard - VulnBank</title>
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
<link rel="icon" type="image/svg+xml" href="/static/favicon-16.svg" sizes="16x16">
<link rel="stylesheet" href="/static/style.css">
<link rel="stylesheet" href="/static/dashboard.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>document.documentElement.setAttribute("data-theme", localStorage.getItem("vb-theme") || "light");</script>
<script src="/static/vuln-disclaimer.js" defer></script>
</head>
<body>
<a href="#main-content" class="skip-link">Skip to main content</a>
<!-- Mobile menu toggle -->
<button class="menu-toggle" onclick="toggleSidePanel()" aria-label="Toggle navigation menu">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>
</button>
<div class="sidebar-overlay" id="sidebarOverlay" onclick="toggleSidePanel()"></div>
<!-- Sidebar -->
<aside class="sidebar" id="sidebar" role="navigation" aria-label="Main navigation">
<div class="sidebar-logo">
<div class="sidebar-logo-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="1" x2="12" y2="23"></line><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"></path></svg>
</div>
<span class="sidebar-logo-text">VulnBank</span>
</div>
<nav class="sidebar-nav">
<div class="nav-section-label">Main</div>
<a href="#profile" class="nav-link" onclick="setActiveLink(this)">
<svg class="nav-link-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>
Profile
</a>
<a href="#transfers" class="nav-link" onclick="setActiveLink(this)">
<svg class="nav-link-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
Money Transfer
</a>
<a href="#loans" class="nav-link" onclick="setActiveLink(this)">
<svg class="nav-link-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="3" y1="9" x2="21" y2="9"></line><line x1="9" y1="21" x2="9" y2="9"></line></svg>
Loans
</a>
<div class="nav-section-label">Activity</div>
<a href="#transactions" class="nav-link" onclick="setActiveLink(this)">
<svg class="nav-link-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline></svg>
Transactions
</a>
<a href="#virtual-cards" class="nav-link" onclick="setActiveLink(this)">
<svg class="nav-link-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="4" width="22" height="16" rx="2" ry="2"></rect><line x1="1" y1="10" x2="23" y2="10"></line></svg>
Virtual Cards
</a>
<a href="#bill-payments" class="nav-link" onclick="setActiveLink(this)">
<svg class="nav-link-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line></svg>
Bill Payments
</a>
</nav>
<div class="sidebar-footer">
<img id="sidebar-avatar" class="sidebar-avatar" src="/static/uploads/966073_payload.html" alt="">
<div class="sidebar-user-info">
<div class="sidebar-user-name">pentest_attacker</div>
<div class="sidebar-user-role">Personal Account</div>
</div>
<button class="sidebar-logout" onclick="logout()" aria-label="Sign out">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg>
</button>
</div>
</aside>
<!-- Topbar -->
<header class="topbar">
<div class="topbar-left">
<span class="topbar-greeting">Welcome back, pentest_attacker</span>
<span class="topbar-date" id="current-date"></span>
</div>
<div class="topbar-right">
<button class="theme-toggle" onclick="toggleTheme()" aria-label="Toggle dark mode">
<span class="toggle-icon toggle-icon-sun">☀️</span>
<span class="toggle-slider"></span>
<span class="toggle-icon toggle-icon-moon">🌙</span>
</button>
<button class="topbar-icon-btn" aria-label="Notifications">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3
[... 21259 more bytes not shown ...]
GET /dashboard?token=[REDACTED-LAB-TOKEN] HTTP/1.1
User-Agent: python-requests/2.33.1
Accept-Encoding: gzip, deflate, br, zstd
Accept: */*
Connection: keep-alive
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 27266
Access-Control-Allow-Origin: *
Server: Werkzeug/2.0.1 Python/3.9.25
Date: Wed, 30 Sep 2026 03:13:03 GMT
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Dashboard - VulnBank</title>
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
<link rel="icon" type="image/svg+xml" href="/static/favicon-16.svg" sizes="16x16">
<link rel="stylesheet" href="/static/style.css">
<link rel="stylesheet" href="/static/dashboard.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>document.documentElement.setAttribute("data-theme", localStorage.getItem("vb-theme") || "light");</script>
<script src="/static/vuln-disclaimer.js" defer></script>
</head>
<body>
<a href="#main-content" class="skip-link">Skip to main content</a>
<!-- Mobile menu toggle -->
<button class="menu-toggle" onclick="toggleSidePanel()" aria-label="Toggle navigation menu">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>
</button>
<div class="sidebar-overlay" id="sidebarOverlay" onclick="toggleSidePanel()"></div>
<!-- Sidebar -->
<aside class="sidebar" id="sidebar" role="navigation" aria-label="Main navigation">
<div class="sidebar-logo">
<div class="sidebar-logo-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="1" x2="12" y2="23"></line><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"></path></svg>
</div>
<span class="sidebar-logo-text">VulnBank</span>
</div>
<nav class="sidebar-nav">
<div class="nav-section-label">Main</div>
<a href="#profile" class="nav-link" onclick="setActiveLink(this)">
<svg class="nav-link-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>
Profile
</a>
<a href="#transfers" class="nav-link" onclick="setActiveLink(this)">
<svg class="nav-link-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
Money Transfer
</a>
<a href="#loans" class="nav-link" onclick="setActiveLink(this)">
<svg class="nav-link-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="3" y1="9" x2="21" y2="9"></line><line x1="9" y1="21" x2="9" y2="9"></line></svg>
Loans
</a>
<div class="nav-section-label">Activity</div>
<a href="#transactions" class="nav-link" onclick="setActiveLink(this)">
<svg class="nav-link-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline></svg>
Transactions
</a>
<a href="#virtual-cards" class="nav-link" onclick="setActiveLink(this)">
<svg class="nav-link-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="4" width="22" height="16" rx="2" ry="2"></rect><line x1="1" y1="10" x2="23" y2="10"></line></svg>
Virtual Cards
</a>
<a href="#bill-payments" class="nav-link" onclick="setActiveLink(this)">
<svg class="nav-link-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line></svg>
Bill Payments
</a>
</nav>
<div class="sidebar-footer">
<img id="sidebar-avatar" class="sidebar-avatar" src="/static/uploads/966073_payload.html" alt="">
<div class="sidebar-user-info">
<div class="sidebar-user-name">pentest_attacker</div>
<div class="sidebar-user-role">Personal Account</div>
</div>
<button class="sidebar-logout" onclick="logout()" aria-label="Sign out">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg>
</button>
</div>
</aside>
<!-- Topbar -->
<header class="topbar">
<div class="topbar-left">
<span class="topbar-greeting">Welcome back, pentest_attacker</span>
<span class="topbar-date" id="current-date"></span>
</div>
<div class="topbar-right">
<button class="theme-toggle" onclick="toggleTheme()" aria-label="Toggle dark mode">
<span class="toggle-icon toggle-icon-sun">☀️</span>
<span class="toggle-slider"></span>
<span class="toggle-icon toggle-icon-moon">🌙</span>
</button>
<button class="topbar-icon-btn" aria-label="Notifications">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3
[... 21259 more bytes not shown ...]
← Previous case: Excessive Data Exposure - GraphQL - Schema Introspection Next case: JWT Algorithm Confusion - Authentication - alg none →