Dekoder JWT

Mengganti input saat ini dengan data demonstrasi.

Hasil yang diharapkan: Header menampilkan HS256 dan payload menampilkan sub = demo-user. Tanda tangan sengaja tidak valid; ini bukan token login.

Batasan: Dekode tidak mengautentikasi token. Verifikasi opsional mendukung HS256/384/512; aplikasi juga harus memvalidasi penerbit, audiens, dan klaim waktu.

Mendekode bukan memverifikasi. Siapa pun bisa membaca payload JWT dan siapa pun bisa membuat token yang terdekode dengan mulus — token yang terdekode menunjukkan apa yang diklaimnya, bukan apakah klaim itu benar. Semuanya berjalan di peramban Anda; token Anda tidak pernah dikirim ke mana pun.

Apa pun yang Anda masukkan di sini tidak disimpan, dicatat, atau dibagikan.

Tempel JWT untuk membaca header dan payload-nya serta melihat kapan ia kedaluwarsa. Proses dekode berjalan sepenuhnya di peramban Anda, jadi token tidak pernah keluar dari perangkat.

Cara memakai

  1. Tempel token ke kolom masukan.
  2. Header, payload, dan klaim waktu muncul di bawah.
  3. Bila perlu, masukkan rahasia untuk memverifikasi tanda tangan HS256 atau HS512.

Membaca tiga segmen

JWT terdiri dari tiga segmen berkode base64url yang digabung dengan titik, dan base64url di sini ditulis tanpa padding, itulah sebabnya segmennya tidak diakhiri tanda sama dengan seperti base64 biasa.

Segmen pertama adalah header: objek JSON kecil yang menyebut algoritma tanda tangan di alg, biasanya tipe di typ, dan kadang identifier kunci di kid agar verifier tahu kunci mana yang dipakai untuk memeriksa. Segmen kedua adalah payload, objek JSON berisi claim. Segmen ketiga adalah tanda tangan, berupa byte mentah, bukan JSON, sehingga di sini ditampilkan dalam heksadesimal.

Tanda tangan mencakup dua segmen pertama persis seperti tampilannya, digabung dengan titik - teks yang sudah dikodekan, bukan JSON yang diwakilinya. Karena itulah token tidak bisa dirapikan, diserialisasi ulang, atau diubah urutan claim-nya tanpa menjadi tidak valid, dan karena itu pula decoder ini menampilkan claim tetapi memperlakukan teks aslinya sebagai hal yang penting.

Mendekode bukan memverifikasi

Inilah hal yang paling sering disalahpahami tentang JWT, jadi perlu dikatakan tanpa ragu. Payload hanya dikodekan, bukan dienkripsi. Siapa pun yang memegang token bisa membaca semua claim di dalamnya. Siapa pun juga bisa membuat token yang payload-nya berisi apa saja, dan token itu akan terdekode sempurna di sini maupun di mana pun.

Token yang didekode hanya memberi Anda sekumpulan klaim tanpa bukti apa pun. Buktinya adalah tanda tangan, dan memeriksanya memerlukan kunci. Sampai pemeriksaan itu dilakukan - dengan kunci dan algoritma yang Anda tentukan sejak awal - token hanya memberi tahu apa yang ingin seseorang Anda percayai.

Versi praktisnya: jangan pernah mengambil keputusan otorisasi dari payload yang hanya didekode. Mendekode token di debugger untuk melihat isinya boleh saja. Tetapi membaca claim sub di kode aplikasi tanpa verifikasi, karena token itu sudah melewati gateway yang seharusnya memeriksanya, adalah cara layanan akhirnya memercayai identitas palsu. Verifikasi HS256 opsional di halaman ini disediakan justru agar perbedaannya terlihat.

Claim, dan siapa yang bertanggung jawab memeriksanya

RFC 7519 mendaftarkan tujuh claim. iss adalah penerbit, sub adalah subjek yang dibicarakan token, aud adalah audiens yang dituju, dan jti adalah identifier unik untuk token itu sendiri. Tiga claim waktu - exp, nbf, dan iat - adalah nilai NumericDate dalam detik sejak epoch Unix, bukan milidetik, dan inilah alasan paling umum token buatan tangan ditolak.

Tidak satu pun dari claim ini berlaku dengan sendirinya. Token dengan exp di masa lalu dianggap kedaluwarsa hanya karena verifier membandingkannya dengan jam lalu menolaknya. Token yang diterbitkan untuk audiens lain hanya berbahaya jika tidak ada yang memeriksa aud - dan beginilah token untuk satu layanan dipakai ulang terhadap layanan lain yang memakai kunci tanda tangan yang sama. Kebanyakan pustaka memeriksa exp secara bawaan, dan aud hanya jika Anda memberi tahu nilai yang diharapkan.

Selisih jam yang kecil antara mesin penerbit dan verifier itu wajar, sehingga verifier biasanya memberi toleransi satu atau dua menit. Halaman ini menampilkan setiap claim waktu sebagai tanggal yang mudah dibaca dan memberi tahu apakah token saat ini masih dalam masa berlakunya.

Kapan JWT bukan pilihan yang tepat

JWT adalah bearer token yang membawa claim-nya sendiri, sehingga sangat cocok untuk panggilan stateless antarlayanan tetapi canggung sebagai mekanisme sesi.

Kecanggungannya ada pada pencabutan. Karena verifier tidak memerlukan apa pun selain kunci, tidak pernah ada saat ia memeriksa penyimpanan, sehingga tidak ada cara membatalkan token sebelum exp. Keluarkan pengguna, ganti kata sandinya, nonaktifkan akunnya: token yang sudah terbit tetap berfungsi sampai kedaluwarsa. Solusi umumnya - masa berlaku sangat singkat dengan refresh token, atau daftar tolak nilai jti yang diperiksa di setiap request - sama-sama mengembalikan state yang semestinya dihilangkan oleh sifat stateless.

Tempat menyimpannya juga penting. Token di localStorage bisa dibaca oleh skrip apa pun yang berjalan di halaman, sehingga satu celah cross-site scripting cukup untuk mencurinya; cookie httpOnly tidak bisa dibaca, dengan konsekuensi memerlukan perlindungan CSRF. Dan karena token dikirim di header pada setiap request, payload yang besar punya biaya nyata - masukkan terlalu banyak claim dan Anda akan menabrak batas ukuran header proxy.

Untuk sesi web biasa, identifier acak yang buram dan terhubung ke penyimpanan di server lebih sederhana, bisa langsung dicabut, dan lebih kecil.

Apa saja yang dilakukan verifikasi, berurutan

Verifikasi yang benar adalah sebuah urutan, dan di langkah yang dilewati itulah serangan-serangan yang sudah dikenal bersarang. Pertama, tentukan algoritmanya di muka dan tolak token mana pun yang header-nya tidak cocok — jangan pernah membaca alg dari token lalu memercayainya. Kedua, pilih kuncinya: untuk secret bersama, kunci itu adalah sebuah konstanta; untuk token asimetris, kuncinya adalah public key yang cocok dengan kid pada header, diambil dari endpoint JWKS milik penerbit lalu disimpan di cache.

Ketiga, hitung ulang signature atas dua segmen pertama persis seperti bentuk aslinya, lalu bandingkan dalam waktu konstan. Keempat, dan baru setelah signature terbukti benar, bacalah claim-nya: cocokkan exp dan nbf dengan jam disertai toleransi kecil, pastikan iss memang penerbit yang Anda harapkan, dan pastikan aud memuat Anda.

Sebagian besar pustaka melakukan semua ini kalau Anda mengonfigurasinya, dan sebagian besar kerentanan muncul karena aplikasi mengerjakan sebagian langkahnya sendiri. Men-decode token untuk membaca sebuah claim sebelum diverifikasi tidak masalah di dalam debugger, tetapi berbahaya di jalur penanganan permintaan, karena pada titik itu token masih sekadar klaim sepihak dari seseorang.

Posisi JWT dalam OAuth dan OpenID Connect

Di OpenID Connect, id_token adalah sebuah JWT dan ia ditujukan untuk klien: ia menyatakan siapa yang masuk, kapan, dan dengan cara apa. Access token adalah hal yang berbeda dan ditujukan untuk API; meski banyak penyedia menerbitkannya sebagai JWT, spesifikasinya tidak mewajibkan itu — ia boleh saja berupa string buram yang divalidasi API lewat introspection.

Pembedaan ini penting karena menentukan apa yang harus dilakukan API Anda. Kalau access token-nya buram, API memanggil endpoint introspection milik penyedia; kalau berupa JWT, API memverifikasinya secara lokal terhadap kunci yang dipublikasikan. Memperlakukan id_token sebagai access token adalah bug integrasi yang umum: token itu diterbitkan untuk audience yang berbeda, dan API yang menerimanya akan ikut menerima token yang dicetak untuk aplikasi mana pun yang dilayani penyedia yang sama.

Scope dan role di dalam token adalah pernyataan dari penerbit, bukan izin. Yang menentukan boleh tidaknya pemanggil melakukan sesuatu tetap API; token hanya menyatakan siapa dia dan sejauh mana penerbit bersedia menjaminnya.

Kerentanan yang terus berulang

Trik alg: none menyatakan sebuah token tidak ditandatangani, dan sepuluh tahun lalu beberapa pustaka benar-benar menerimanya. Kebingungan algoritma lebih halus: ambil token RS256, ubah header-nya menjadi HS256, lalu tandatangani dengan public key milik penerbit sebagai secret HMAC — verifier yang membaca algoritma dari token akan menerimanya, karena public key memang bukan rahasia.

Secret bersama yang lemah adalah temuan paling sering di dunia nyata. Sebuah JWT sudah memuat semua yang dibutuhkan untuk menguji kandidat secret secara offline, secepat yang dimungkinkan perangkat keras penyerang, sehingga kata yang ada di kamus jebol dalam hitungan detik dan siapa pun bisa mencetak token dengan claim sesuka hatinya.

Lalu ada kesalahan logika: pemeriksaan aud yang terlewat sehingga token milik satu layanan bisa dipakai untuk masuk ke layanan lain yang berbagi kunci yang sama; parameter kid yang dipakai memuat kunci dari sebuah path tanpa membersihkan masukannya; dan token yang diterima lewat query string, sehingga berakhir di log server, riwayat peramban, dan header referrer.

Session atau token untuk aplikasi web

Untuk satu aplikasi web dengan backend sendiri, session di sisi server dengan pengenal buram di dalam cookie lebih sederhana dan lebih baik hampir di setiap sisi: pencabutan berlaku seketika, cookie-nya kecil, isinya tidak pernah meninggalkan server, dan tidak ada penandatanganan yang bisa salah. Harganya adalah kebutuhan akan penyimpanan session, yang bagi kebanyakan aplikasi cukup berupa satu tabel atau satu key di Redis.

JWT baru benar-benar berguna ketika pihak yang memverifikasi tidak bisa bertanya kepada penerbit: panggilan antarlayanan yang melewati batas kepercayaan, API yang dipakai pihak ketiga, atau edge tanpa state yang harus memvalidasi permintaan tanpa perjalanan bolak-balik. Itu semua kendala nyata, dan kalau Anda memang menghadapinya, kerumitan tambahannya sepadan.

Jalan tengah yang biasanya dipilih tim adalah access token berumur pendek — hitungan menit, bukan jam — berpasangan dengan refresh token berumur lebih panjang yang disimpan di cookie httpOnly sekaligus di sisi server agar bisa dicabut. Dengan begitu jalur yang umum tetap mendapat verifikasi tanpa state, sementara Anda masih punya tempat untuk berkata tidak.

Di mana menyimpan token di peramban

localStorage bisa dibaca skrip apa pun yang berjalan di halaman itu, jadi satu celah cross-site scripting di mana pun pada origin yang sama — termasuk di skrip analitik atau iklan pihak ketiga — sudah cukup untuk membocorkan token. Inilah pilihan bawaan di banyak tutorial, dan sekaligus yang paling lemah di antara opsi yang ada.

Cookie dengan httpOnly, Secure, dan SameSite sama sekali tidak bisa dibaca skrip, sehingga seluruh kelas serangan itu hilang, tetapi sebagai gantinya muncul kebutuhan akan perlindungan CSRF, karena peramban melampirkan cookie itu secara otomatis. SameSite=Lax mengatasi sebagian besarnya dan token anti-pemalsuan menutup sisanya.

Menyimpan token hanya di memori — sebagai variabel di dalam aplikasi, diambil ulang saat halaman dimuat melalui endpoint yang diautentikasi lewat cookie — adalah pola umum yang paling kuat sekaligus paling merepotkan, karena setiap muat ulang menuntut satu perjalanan refresh. Apa pun pilihannya, token tetap ikut terkirim di header pada setiap permintaan, jadi menjaga payload tetap kecil adalah keputusan performa sekaligus privasi.

Pertanyaan umum

Apakah berhasil mendekode JWT berarti token itu valid?
Tidak, dan inilah hal terpenting yang perlu dipahami tentang JWT. Payload dikodekan dengan base64url, bukan dienkripsi atau dilindungi: siapa pun bisa membacanya, dan siapa pun bisa membuat token yang terdekode dengan mulus. Hanya verifikasi tanda tangan dengan kunci yang benar yang membuktikan token itu asli dan tidak diubah.
Apakah aman menempelkan token asli di sini?
Dekode dan verifikasi sama-sama berjalan dengan JavaScript di halaman ini. Tidak ada yang diunggah, dicatat, atau dikirim. Meski begitu, perlakukan setiap JWT sebagai kredensial aktif: kalau ia sudah bocor di tempat lain, cabut token itu alih-alih mengandalkan tempat Anda menempelkannya.
Kenapa hanya HS256 dan HS512 yang bisa diverifikasi?
Keduanya simetris: rahasia yang sama dipakai untuk menandatangani dan memverifikasi, jadi pemeriksaan bisa dilakukan lokal dengan apa yang Anda berikan. RS, ES, dan PS bersifat asimetris dan memerlukan kunci publik penerbit, yang biasanya diambil dari endpoint JWKS, dan itu pekerjaan yang berbeda.
Apa arti exp, iat, dan nbf?
Ketiganya klaim terdaftar yang berisi NumericDate, diukur dalam detik sejak epoch Unix, bukan milidetik. exp adalah saat token berhenti berlaku, iat saat ia diterbitkan, dan nbf saat paling awal ia boleh dipakai. Alat ini menampilkan masing-masing sebagai tanggal yang mudah dibaca.
Apa arti alg: none?
Itu menyatakan token tanpa tanda tangan. Pustaka yang menuruti nilai ini bisa ditipu untuk menerima token palsu, jadi ini pola serangan yang sudah dikenal dan seharusnya ditolak oleh setiap verifikator. Alat ini tidak akan pernah melaporkan token semacam itu sebagai terverifikasi.

Terakhir diperbarui:

Referensi

RFC 7519 — JSON Web Token

Perubahan terbaru

  • — Menambahkan contoh yang dapat dijalankan, hasil yang diharapkan, batasan, dan referensi.

Laporkan masalah

Sertakan langkah, hasil yang diharapkan, dan hasil sebenarnya. Hapus rahasia dan data pribadi.

Alat terkait