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)

01 / 2
02 / 3

03 — Detail proses

Detail proses

Nilai kredensial disamarkan dalam tampilan HTML ini; keterangan tentang token lengkap merujuk pada rekaman sumber.

PROSES 01 / 1
01 / Periksa masa berlaku JWT

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, username

Klaim kedaluwarsa (exp)

absent

Nilai 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
HTTP request
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 response
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
HTTP request
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 response
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 ...]