Keamanan JSON Web Token: Kerentanan Umum dan Perbaikannya
JWT adalah standar de facto untuk autentikasi API, tetapi kesederhanaannya yang tampak menyembunyikan jebakan keamanan yang nyata. JWT yang salah dikonfigurasi telah menyebabkan bypass autentikasi, eskalasi hak istimewa, dan kebocoran data. Panduan ini membahas kerentanan paling kritis dan cara mencegahnya.
Ringkasan Struktur JWT
JWT terdiri dari tiga bagian yang dienkode Base64URL yang dipisahkan oleh titik:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMiLCJyb2xlIjoiYWRtaW4ifQ.signature
- Header: Algoritma dan tipe token
- Payload: Klaim (data pengguna, kedaluwarsa, dll.)
- Signature: Verifikasi kriptografis
Periksa struktur JWT dengan JWT Encoder/Decoder kami.
Untuk pemahaman dasar tentang struktur JWT, lihat panduan JWT Tokens Explained kami.
Kerentanan Kritis
1. Serangan Kebingungan Algoritma
Kerentanan JWT paling berbahaya. Jika server menerima header alg dari token tanpa validasi, penyerang dapat:
Serangan: Mengubah algoritma dari RS256 (asimetris) ke HS256 (simetris) dan menandatangani token palsu dengan kunci publik server:
// Attacker's forged token
header: { "alg": "HS256", "typ": "JWT" }
payload: { "sub": "admin", "role": "superadmin" }
// Signed with the server's PUBLIC key as the HMAC secret
Jika server memverifikasi token HS256 menggunakan kunci publik sebagai rahasia, token palsu lolos verifikasi.
Perbaikan: Selalu tentukan algoritma yang diharapkan secara eksplisit:
// WRONG - accepts whatever algorithm the token specifies
jwt.verify(token, key);
// CORRECT - enforce specific algorithm
jwt.verify(token, key, { algorithms: ['RS256'] });
2. Serangan Algoritma None
Beberapa library menerima "alg": "none" — token tanpa tanda tangan:
// Forged token with no signature
header: { "alg": "none", "typ": "JWT" }
payload: { "sub": "admin", "role": "superadmin" }
signature: "" // empty
Perbaikan: Jangan pernah mengizinkan algoritma none di produksi. Tentukan secara eksplisit daftar algoritma yang diizinkan.
3. Rahasia Penandatanganan yang Lemah
JWT berbasis HMAC (HS256/HS384/HS512) hanya sekuat rahasianya:
// TERRIBLE - can be brute-forced in seconds
secret = "password123"
// WEAK - dictionary attack vulnerable
secret = "my-jwt-secret"
// STRONG - 256+ bits of randomness
secret = "a3f2b8c9d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0"
Perbaikan: Gunakan minimal 256 bit keacakan kriptografis untuk rahasia HMAC. Lebih baik lagi, gunakan kunci asimetris (RS256, ES256) di mana kunci penandatanganan tidak perlu dibagikan.
4. Kedaluwarsa yang Hilang
Token tanpa kedaluwarsa tidak pernah kedaluwarsa — token yang dicuri memberikan akses permanen:
// WRONG - no expiration
{ "sub": "user123", "role": "admin" }
// CORRECT - short expiration
{
"sub": "user123",
"role": "admin",
"exp": 1705312800,
"iat": 1705309200,
"nbf": 1705309200
}
Praktik terbaik: Token akses kedaluwarsa dalam 15-60 menit. Gunakan refresh token (disimpan dengan aman) untuk sesi yang panjang.
5. Data Sensitif di Payload
Payload JWT dienkode Base64URL, bukan dienkripsi. Siapa pun dapat mendekodenya:
echo "eyJzdWIiOiIxMjMiLCJyb2xlIjoiYWRtaW4ifQ" | base64 -d
# {"sub":"123","role":"admin"}
Jangan pernah menyertakan: Kata sandi, kunci API, nomor kartu kredit, data pribadi (SSN, catatan medis), atau rahasia apa pun di payload JWT.
6. Penyimpanan Token (XSS vs CSRF)
| Penyimpanan | Rentan XSS | Rentan CSRF |
|---|---|---|
| localStorage | Ya | Tidak |
| Cookie (tanpa flag) | Ya | Ya |
| Cookie HttpOnly | Tidak | Ya |
| Cookie HttpOnly + SameSite | Tidak | Tidak |
Direkomendasikan: Simpan JWT di cookie HttpOnly, Secure, SameSite=Strict. Ini mencegah akses JavaScript (pertahanan XSS) dan permintaan lintas situs (pertahanan CSRF).
Set-Cookie: token=eyJ...; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600
Daftar Periksa Keamanan
- Tentukan algoritma secara eksplisit — jangan pernah menerima dari header token
- Tolak algoritma
none— selalu wajibkan tanda tangan - Gunakan rahasia yang kuat — 256+ bit untuk HMAC, 2048+ bit untuk RSA
- Atur kedaluwarsa singkat — 15-60 menit untuk token akses
- Validasi semua klaim —
exp,iss,aud,nbf - Simpan di cookie HttpOnly — bukan localStorage
- Rotasi rahasia secara berkala — rencanakan rotasi kunci
- Jangan pernah menyimpan data sensitif di payload
- Gunakan kunci asimetris untuk sistem terdistribusi (RS256 atau ES256)
- Implementasikan pencabutan token — daftar hitam atau kedaluwarsa singkat + refresh token
Penandatanganan Asimetris vs Simetris
| Aspek | HS256 (Simetris) | RS256 (Asimetris) |
|---|---|---|
| Kunci | Rahasia bersama | Pasangan kunci privat/publik |
| Siapa yang bisa menandatangani | Siapa pun yang memiliki rahasia | Hanya pemegang kunci privat |
| Siapa yang bisa memverifikasi | Siapa pun yang memiliki rahasia | Siapa pun yang memiliki kunci publik |
| Distribusi kunci | Rahasia harus dibagikan dengan aman | Hanya kunci publik yang dibagikan |
| Performa | Lebih cepat | Lebih lambat |
| Terbaik untuk | Layanan tunggal | Microservices, sistem terdistribusi |
Rekomendasi: Gunakan ES256 (ECDSA) untuk aplikasi baru — memberikan keamanan asimetris dengan performa mendekati HMAC.
Strategi Pencabutan Token
JWT bersifat stateless secara desain — tidak ada cara bawaan untuk mencabutnya. Strateginya:
- Kedaluwarsa singkat: Jika token kedaluwarsa dalam 15 menit, jendela token yang dicuri terbatas
- Rotasi refresh token: Terbitkan refresh token baru setiap kali digunakan; jika refresh token digunakan dua kali, cabut semua token
- Daftar hitam: Simpan ID token yang dicabut (klaim jti) dan periksa pada setiap permintaan
- Versi token: Sertakan nomor versi dalam klaim; tingkatkan versi pengguna saat logout
FAQ
Haruskah saya menggunakan JWT atau cookie sesi untuk autentikasi?
Cookie sesi lebih sederhana dan lebih aman untuk aplikasi web tradisional — server mengontrol siklus hidup sesi, dan pencabutan bersifat instan. JWT lebih cocok untuk API stateless, microservices, dan aplikasi mobile di mana penyimpanan sesi sisi server tidak praktis. Jika Anda memilih JWT, terapkan langkah-langkah keamanan dalam panduan ini.
Berapa waktu kedaluwarsa JWT yang ideal?
Untuk token akses: 15-60 menit. Lebih singkat lebih aman tetapi memerlukan refresh lebih sering. Untuk refresh token: 1-30 hari, disimpan di cookie HttpOnly. Untuk token sekali pakai (verifikasi email, reset kata sandi): 1-24 jam.
Sumber Terkait
- JWT Encoder/Decoder — Periksa dan dekode JWT dengan aman
- JWT Tokens Explained — Pahami struktur dan alur JWT
- Password Entropy Explained — Kekuatan rahasia penandatanganan JWT