Skenario 08 / 26FAIL
Insufficient Session Expiration - Authentication - JWT Lifetime
Lingkungan lab — bukan sistem produksi
Tangkapan layar dan trafik mentah tidak diterjemahkan; teks di dalamnya mungkin menggunakan bahasa lain.
vuln-bank/Evidence/[FAIL] Insufficient Session Expiration - Authentication - JWT Lifetime
01 — Catatan
Catatan pengujian
Kerentanan
[FAIL] Insufficient Session Expiration - Authentication - JWT Lifetime
Deskripsi
Token JWT yang diterbitkan tidak memuat klaim exp sehingga tidak pernah kedaluwarsa, dan tidak tersedia mekanisme pencabutan token. Selain itu token diterima dari beberapa lokasi sekaligus, termasuk parameter query URL, dan pada antarmuka web disimpan di localStorage sehingga terbaca oleh JavaScript.
Dampak
Medium: token yang pernah bocor berlaku selamanya. Tidak ada rotasi maupun pencabutan, sehingga satu kali kebocoran memberi akses permanen ke akun korban. Penerimaan token melalui parameter query memperbesar peluang kebocoran karena URL tercatat di riwayat peramban, log server, dan header Referer. Penyimpanan di localStorage membuat token dapat dicuri oleh XSS, dan hal ini telah dibuktikan pada temuan Stored Cross-Site Scripting di mana JWT administrator berhasil diambil.
CWE
CWE-613: Insufficient Session Expiration
OWASP
A07:2021 - Identification and Authentication Failures
Skenario
Analisis token: token yang diterbitkan didekode dan hanya memuat klaim user_id, username, is_admin, dan iat; klaim exp tidak ada sama sekali. Verifikasi: token yang sama tetap diterima pada endpoint terproteksi. Pendukung: token juga diterima ketika dikirim sebagai parameter query pada URL, bukan hanya pada header Authorization.
Endpoint
POST http://127.0.0.1:5001/login (penerbitan token)
GET http://127.0.0.1:5001/dashboard (penerimaan token)
Fitur terdampak
Manajemen sesi berbasis JWT
Rekomendasi perbaikan
- Tambahkan klaim exp dengan masa berlaku singkat dan terapkan refresh token yang dapat dicabut.
- Simpan daftar pencabutan (denylist) atau gunakan token identifier agar sesi dapat dimatikan saat terjadi insiden.
- Terima token hanya dari header Authorization; hentikan pembacaan dari parameter query dan form.
- Pindahkan token sesi ke cookie HttpOnly, Secure, dan SameSite agar tidak terjangkau JavaScript.
Bukti konsep
1) (Screenshot 1) ANALISIS: token hasil login didekode -> hanya memuat user_id, username, is_admin, dan iat; klaim exp tidak ada.
2) (Screenshot 2) VERIFIKASI: token yang sama diterima pada endpoint terproteksi -> HTTP 200.
3) (Screenshot 3) DAMPAK: token yang sama juga diterima ketika dikirim sebagai parameter query pada URL -> HTTP 200, memperbesar peluang kebocoran melalui log dan Referer.
Lihat catatan sumber (bahasa Indonesia)
Vulnerability:
[FAIL] Insufficient Session Expiration - Authentication - JWT Lifetime
Description:
Token JWT yang diterbitkan tidak memuat klaim exp sehingga tidak pernah kedaluwarsa, dan tidak tersedia mekanisme pencabutan token. Selain itu token diterima dari beberapa lokasi sekaligus, termasuk parameter query URL, dan pada antarmuka web disimpan di localStorage sehingga terbaca oleh JavaScript.
Impact:
Medium: token yang pernah bocor berlaku selamanya. Tidak ada rotasi maupun pencabutan, sehingga satu kali kebocoran memberi akses permanen ke akun korban. Penerimaan token melalui parameter query memperbesar peluang kebocoran karena URL tercatat di riwayat peramban, log server, dan header Referer. Penyimpanan di localStorage membuat token dapat dicuri oleh XSS, dan hal ini telah dibuktikan pada temuan Stored Cross-Site Scripting di mana JWT administrator berhasil diambil.
CWE:
CWE-613: Insufficient Session Expiration
OWASP:
A07:2021 - Identification and Authentication Failures
Scenario:
Analisis token: token yang diterbitkan didekode dan hanya memuat klaim user_id, username, is_admin, dan iat; klaim exp tidak ada sama sekali. Verifikasi: token yang sama tetap diterima pada endpoint terproteksi. Pendukung: token juga diterima ketika dikirim sebagai parameter query pada URL, bukan hanya pada header Authorization.
Endpoint:
POST http://127.0.0.1:5001/login (penerbitan token)
GET http://127.0.0.1:5001/dashboard (penerimaan token)
Affected Feature:
Manajemen sesi berbasis JWT
Remediation:
- Tambahkan klaim exp dengan masa berlaku singkat dan terapkan refresh token yang dapat dicabut.
- Simpan daftar pencabutan (denylist) atau gunakan token identifier agar sesi dapat dimatikan saat terjadi insiden.
- Terima token hanya dari header Authorization; hentikan pembacaan dari parameter query dan form.
- Pindahkan token sesi ke cookie HttpOnly, Secure, dan SameSite agar tidak terjangkau JavaScript.
POC:
1) (Screenshot 1) ANALISIS: token hasil login didekode -> hanya memuat user_id, username, is_admin, dan iat; klaim exp tidak ada.
2) (Screenshot 2) VERIFIKASI: token yang sama diterima pada endpoint terproteksi -> HTTP 200.
3) (Screenshot 3) DAMPAK: token yang sama juga diterima ketika dikirim sebagai parameter query pada URL -> HTTP 200, memperbesar peluang kebocoran melalui log dan Referer.
02 — Tangkapan layar
Tangkapan layar (2)
03 — Detail proses
Detail proses
Nilai kredensial disamarkan dalam tampilan HTML ini; keterangan tentang token lengkap merujuk pada rekaman sumber.
PROSES 01 / 1
Periksa masa berlaku JWT
Token yang diterbitkan
[REDACTED-LAB-JWT]Payload yang didekode
{
"user_id": 2,
"username": "pentest_attacker",
"is_admin": false,
"iat": 1790737980
}Klaim yang tersedia
iat, is_admin, user_id, usernameKlaim kedaluwarsa (exp)
absentNilai kredensial disamarkan dalam tampilan HTML ini; keterangan tentang token lengkap merujuk pada rekaman sumber.
04 — Request dan response
Rekaman HTTP
Sebagian nilai kredensial disamarkan pada tampilan HTML publik ini. Penyamaran ini tidak menjamin bahwa seluruh data sensitif atau berkas tertaut sudah disamarkan.
REKAMAN 01 / 2
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 ...]REKAMAN 02 / 3
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 ...]