Manajemen sesi untuk aplikasi web: cookie vs JWT vs refresh
Perbandingan manajemen sesi untuk web: sesi berbasis cookie, JWT, dan refresh token, dengan model ancaman konkret dan persyaratan logout realistis.

Apa yang sebenarnya dilakukan manajemen sesi
Sesi adalah bagaimana aplikasi Anda menjawab satu pertanyaan setelah seseorang masuk: "Siapa Anda sekarang?" Setelah jawaban itu bisa dipercaya, aplikasi bisa memutuskan apa yang boleh dilihat pengguna, apa yang boleh mereka ubah, dan tindakan apa yang harus diblokir.
"Tetap masuk" juga merupakan pilihan keamanan. Anda memutuskan berapa lama identitas pengguna harus tetap valid, di mana bukti identitas itu disimpan, dan apa yang terjadi jika bukti itu disalin.
Sebagian besar konfigurasi web app mengandalkan tiga blok bangunan:
- Cookie-based server sessions: browser menyimpan cookie, dan server mencari sesi pada setiap permintaan.
- JWT access tokens: klien mengirim token yang ditandatangani yang bisa diverifikasi server tanpa lookup database.
- Refresh tokens: kredensial berdurasi lebih panjang yang digunakan untuk mendapatkan access token singkat baru.
Ini bukan sekadar "gaya" yang bersaing, melainkan cara berbeda untuk menangani trade-off yang sama: kecepatan vs kontrol, kesederhanaan vs fleksibilitas, dan "bisakah kita mencabut ini sekarang?" vs "apakah ini kedaluwarsa sendiri?"
Cara yang berguna untuk menilai desain apa pun: jika penyerang mencuri apa pun yang digunakan aplikasi Anda sebagai bukti (cookie atau token), apa yang bisa mereka lakukan, dan berapa lama? Sesi berbasis cookie sering unggul ketika Anda butuh kontrol sisi-server yang kuat, seperti forced logout atau immediate lockout. JWT cocok untuk pemeriksaan stateless di banyak layanan, tetapi jadi merepotkan ketika Anda butuh pencabutan segera.
Tidak ada satu opsi yang menang di semua tempat. Pendekatan yang tepat tergantung pada model ancaman Anda, seberapa ketat persyaratan logout, dan seberapa banyak kompleksitas yang bisa dipelihara tim Anda.
Model ancaman yang mengubah jawaban yang tepat
Desain sesi yang baik bergantung lebih pada serangan mana yang benar-benar perlu Anda tangkal daripada "jenis token terbaik."
Jika penyerang mencuri data dari penyimpanan browser (seperti localStorage), JWT access token mudah diambil karena JavaScript halaman bisa membacanya. Cookie yang dicuri berbeda: jika diatur sebagai HttpOnly, kode halaman biasa tidak bisa membacanya, sehingga serangan "ambil token" sederhana jadi lebih sulit. Tetapi jika penyerang punya akses perangkat (laptop hilang, malware, komputer bersama), cookie masih bisa disalin dari profil browser.
XSS (kode penyerang berjalan di halaman Anda) mengubah segalanya. Dengan XSS, penyerang mungkin tak perlu mencuri apa pun. Mereka bisa menggunakan sesi yang sudah masuk untuk melakukan aksi. HttpOnly cookie membantu mencegah pembacaan rahasia sesi, tapi itu tidak menghentikan penyerang membuat permintaan dari halaman.
CSRF (situs lain memicu aksi yang tidak diinginkan) terutama mengancam sesi berbasis cookie, karena browser otomatis melampirkan cookie. Jika Anda mengandalkan cookie, Anda memerlukan pertahanan CSRF yang jelas: pengaturan SameSite yang disengaja, token anti-CSRF, dan penanganan permintaan yang mengubah state dengan hati-hati. JWT yang dikirim di header Authorization lebih sedikit terekspos pada CSRF klasik, tetapi tetap terekspos ke XSS jika disimpan di tempat yang bisa dibaca JavaScript.
Replay attacks (mengulang penggunaan kredensial yang dicuri) adalah area di mana sesi sisi-server unggul: Anda bisa mencabut session ID segera. JWT singkat mengurangi waktu replay, tetapi tidak menghentikan replay selama token masih valid.
Perangkat bersama dan ponsel hilang menjadikan "sign out" sebagai model ancaman nyata. Keputusan biasanya bergantung pada pertanyaan seperti: dapatkah pengguna memaksa logout dari perangkat lain, seberapa cepat harus berlaku, apa yang terjadi jika refresh token dicuri, dan apakah Anda mengizinkan sesi "remember me"? Banyak tim juga menempatkan akses staf pada standar yang lebih ketat daripada akses pelanggan, yang mengubah timeout dan ekspektasi pencabutan.
Sesi cookie: cara kerjanya dan apa yang dilindungi
Sesi berbasis cookie adalah pengaturan klasik. Setelah sign-in, server membuat record sesi (sering ID plus field seperti user ID, waktu dibuat, dan expiry). Browser hanya menyimpan session ID di cookie. Pada setiap permintaan, browser mengirim cookie itu kembali, dan server mencari sesi untuk memutuskan siapa pengguna.
Keuntungan keamanan besar adalah kontrol. Sesi diverifikasi di server setiap kali. Jika Anda perlu menendang seseorang keluar, Anda menghapus atau menonaktifkan record sesi sisi-server dan itu berhenti bekerja segera, bahkan jika pengguna masih punya cookie.
Banyak perlindungan berasal dari pengaturan cookie:
- HttpOnly: mencegah JavaScript membaca cookie.
- Secure: mengirim cookie hanya lewat HTTPS.
- SameSite: membatasi kapan browser mengirim cookie pada permintaan lintas-situs.
Tempat Anda menyimpan state sesi memengaruhi skala. Menyimpan sesi di memori aplikasi sederhana, tetapi rusak saat Anda menjalankan beberapa server atau sering restart. Database bekerja dengan baik untuk durability. Redis umum saat Anda ingin lookup cepat dan banyak sesi aktif. Intinya: server harus bisa menemukan dan memvalidasi sesi pada setiap permintaan.
Sesi cookie cocok ketika Anda butuh perilaku logout yang ketat, seperti dashboard staf atau portal pelanggan di mana admin harus bisa memaksa logout setelah perubahan peran. Jika seorang pegawai keluar, menonaktifkan sesi sisi-server mereka menghentikan akses segera, tanpa menunggu token kedaluwarsa.
JWT access tokens: kekuatan dan sisi tajamnya
JWT (JSON Web Token) adalah string yang ditandatangani yang membawa beberapa klaim tentang pengguna (seperti user ID, peran, tenant) plus waktu kedaluwarsa. API Anda memverifikasi signature dan expiry secara lokal, tanpa memanggil database, lalu memberi otorisasi permintaan.
Itulah mengapa JWT populer di produk API-first, aplikasi mobile, dan sistem di mana beberapa layanan perlu memvalidasi identitas yang sama. Jika Anda punya banyak instance backend, masing-masing bisa memverifikasi token yang sama dan mendapatkan jawaban yang sama.
Kekuatan
JWT access token cepat untuk dicek dan mudah diteruskan bersama panggilan API. Jika frontend Anda memanggil banyak endpoint, access token berumur pendek bisa menjaga alur tetap sederhana: verifikasi signature, baca user ID, lanjutkan.
Contoh: portal pelanggan memanggil "List invoices" dan "Update profile" pada layanan terpisah. JWT bisa membawa customer ID dan peran seperti customer, sehingga tiap layanan bisa mengotorisasi permintaan tanpa lookup sesi setiap kali.
Sisi tajam
Trade-off terbesar adalah pencabutan. Jika token valid selama satu jam, biasanya token itu valid di mana-mana selama satu jam itu, bahkan jika pengguna menekan "log out" atau admin menonaktifkan akun, kecuali Anda menambahkan pengecekan sisi-server tambahan.
JWT juga bocor dengan cara biasa. Titik kegagalan umum termasuk localStorage (XSS bisa membacanya), memori browser (ekstensi jahat), log dan laporan error, proxy dan alat analytics yang menangkap header, dan token yang disalin di chat dukungan atau screenshot.
Karena itu, JWT access token paling cocok untuk akses yang berumur pendek, bukan "login selamanya." Buat mereka seminimal mungkin (jangan masukkan data pribadi sensitif), buat expiry singkat, dan anggap token yang dicuri akan dapat digunakan sampai kedaluwarsa.
Refresh tokens: membuat setup JWT layak digunakan
JWT access token dimaksudkan untuk berumur pendek. Itu baik untuk keselamatan, tetapi menimbulkan masalah praktis: pengguna seharusnya tidak perlu masuk lagi setiap beberapa menit. Refresh token menyelesaikan itu dengan memungkinkan aplikasi secara diam-diam mendapatkan access token baru saat yang lama kedaluwarsa.
Di mana Anda menyimpan refresh token lebih penting daripada tempat menyimpan access token. Dalam web app berbasis browser, default paling aman adalah cookie HttpOnly dan Secure sehingga JavaScript tidak bisa membacanya. Local storage lebih mudah diimplementasikan, tetapi lebih mudah dicuri jika Anda punya bug XSS. Jika model ancaman Anda termasuk XSS, hindari menaruh rahasia berdurasi panjang di storage yang bisa diakses JavaScript.
Rotasi membuat refresh token layak di sistem nyata. Alih-alih menggunakan refresh token yang sama selama berminggu-minggu, Anda menukarnya setiap kali dipakai: klien mengirim refresh token A, server mengeluarkan access token baru plus refresh token B, dan refresh token A menjadi tidak valid.
Setup rotasi sederhana biasanya mengikuti beberapa aturan:
- Jaga access token pendek (menit, bukan jam).
- Simpan refresh token di sisi-server dengan status dan waktu terakhir dipakai.
- Rotasi setiap kali refresh dan batalkan token sebelumnya.
- Kaitkan refresh token ke perangkat atau browser bila memungkinkan.
- Catat kejadian refresh agar Anda bisa menyelidiki penyalahgunaan.
Deteksi reuse adalah alarm kunci. Jika refresh token A sudah dipakai untuk ditukar, tetapi Anda melihatnya lagi kemudian, anggap itu telah disalin. Respons umum adalah mencabut seluruh sesi (dan sering semua sesi untuk pengguna itu) dan meminta login ulang, karena Anda tidak bisa tahu salinan mana yang asli.
Untuk logout, Anda perlu sesuatu yang bisa ditegakkan server. Itu biasanya berarti tabel sesi (atau daftar pencabutan) yang menandai refresh token dicabut. Access token mungkin masih bekerja sampai kedaluwarsa, tetapi Anda bisa menjaga jendela itu kecil dengan membuat access token berumur pendek.
Persyaratan logout dan apa yang sebenarnya dapat ditegakkan
Logout terdengar sederhana sampai Anda mendefinisikannya. Biasanya ada dua permintaan berbeda: "log out perangkat ini" (satu browser atau satu ponsel) dan "log out di mana saja" (semua sesi aktif di semua perangkat).
Ada juga pertanyaan waktu. "Logout segera" berarti aplikasi berhenti menerima kredensial sekarang juga. "Logout setelah kedaluwarsa" berarti aplikasi berhenti menerimanya saat sesi atau token saat ini kedaluwarsa secara alami.
Dengan sesi berbasis cookie, logout segera mudah karena server menguasai sesi. Anda menghapus cookie di klien dan menonaktifkan record sesi sisi-server. Jika seseorang menyalin nilai cookie sebelumnya, penolakan server yang benar-benar menegakkan logout.
Dengan otentikasi hanya JWT (token akses stateless dan tanpa lookup server), Anda tidak bisa benar-benar menjamin logout segera. JWT yang dicuri tetap valid sampai kedaluwarsa, karena server tidak punya tempat untuk memeriksa "apakah token ini dicabut?" Anda bisa menambahkan denylist, tetapi itu berarti menyimpan state dan memeriksanya, yang menghilangkan banyak kesederhanaan awal.
Pola praktis adalah menganggap access token berumur pendek dan menegakkan logout lewat refresh token. Access token dibiarkan berjalan beberapa menit, tetapi refresh token yang menjaga sesi tetap hidup. Jika laptop dicuri, mencabut keluarga refresh token memutus akses masa depan dengan cepat.
Yang realistis bisa Anda janjikan kepada pengguna:
- Logout perangkat ini: cabut sesi atau refresh token itu, dan hapus cookie atau storage lokal.
- Logout di mana saja: cabut semua sesi atau semua keluarga refresh token untuk akun itu.
- Efek "segera": dijamin dengan sesi server, best-effort dengan access token sampai kedaluwarsa.
- Peristiwa forced logout: ganti kata sandi, nonaktifkan akun, turunkan peran.
Untuk perubahan kata sandi dan penonaktifan akun, jangan mengandalkan "pengguna akan keluar." Simpan versi sesi di seluruh akun (atau cap waktu "token valid after"). Pada setiap refresh (dan kadang pada setiap permintaan), bandingkan. Jika berubah, tolak dan minta masuk lagi.
Langkah demi langkah: memilih pendekatan sesi untuk aplikasi Anda
Jika Anda ingin desain sesi tetap sederhana, putuskan aturan Anda terlebih dahulu baru pilih mekaniknya. Sebagian besar masalah dimulai ketika tim memilih JWT atau cookie karena populer, bukan karena cocok dengan risiko dan persyaratan logout.
Mulailah dengan mencantumkan setiap tempat pengguna masuk. Aplikasi browser berperilaku berbeda dari aplikasi mobile native, alat admin internal, atau integrasi mitra. Masing-masing mengubah apa yang bisa disimpan dengan aman, bagaimana login diperbarui, dan apa arti "logout."
Urutan praktis yang bekerja untuk kebanyakan tim:
- Daftar klien Anda: web, iOS/Android, alat internal, akses pihak ketiga.
- Pilih model ancaman default: XSS, CSRF, perangkat dicuri.
- Putuskan apa yang harus dijamin logout: perangkat ini, semua perangkat, forced logout admin.
- Pilih pola dasar: sesi berbasis cookie (server mengingat) atau access token + refresh token.
- Tetapkan timeout dan aturan respons: idle vs expiry absolut, plus apa yang dilakukan saat melihat reuse mencurigakan.
Kemudian dokumentasikan janji tepat sistem Anda. Contoh: "Sesi web kedaluwarsa setelah 30 menit idle atau 7 hari absolut. Admin dapat memaksa logout dalam 60 detik. Ponsel yang hilang dapat dinonaktifkan dari jarak jauh." Kalimat-kalimat itu lebih penting daripada library yang Anda gunakan.
Terakhir, tambahkan monitoring yang cocok dengan pola Anda. Untuk setup token, sinyal kuat adalah reuse refresh token (refresh token yang sama dipakai dua kali). Perlakukan itu sebagai kemungkinan pencurian, cabut keluarga sesi, dan beri tahu pengguna.
Kesalahan umum yang menyebabkan pengambilalihan akun
Sebagian besar pengambilalihan akun bukanlah "serangan pintar." Mereka kemenangan sederhana akibat kesalahan sesi yang bisa diprediksi. Penanganan sesi yang baik kebanyakan soal tidak memberi penyerang cara mudah mencuri atau memutar ulang kredensial.
Salah satu jebakan umum adalah meletakkan access token di localStorage dan berharap Anda tidak pernah punya XSS. Jika ada skrip yang berjalan di halaman Anda (dependensi yang buruk, widget yang disuntikkan, komentar tersimpan), skrip itu bisa membaca localStorage dan mengirim token keluar. Cookie dengan flag HttpOnly mengurangi risiko itu karena JavaScript tidak dapat membacanya.
Jebakan lain adalah membuat JWT berumur panjang untuk menghindari refresh token. Access token 7 hari adalah jendela reuse 7 hari jika bocor. Access token singkat plus refresh token yang dikelola dengan baik lebih sulit disalahgunakan, terutama bila Anda dapat memutus refresh.
Cookie membawa risiko sendiri: lupa pertahanan CSRF. Jika aplikasi Anda menggunakan sesi cookie dan menerima permintaan yang mengubah state tanpa proteksi CSRF, situs jahat bisa mengelabui browser yang sedang login untuk mengirim permintaan valid.
Kesalahan lain yang sering muncul setelah review insiden:
- Refresh token tidak pernah dirotasi, atau dirotasi tetapi Anda tidak mendeteksi reuse.
- Anda mendukung banyak metode login (sesi cookie dan bearer token) tetapi aturan server "yang mana yang menang" tidak jelas.
- Token berakhir di log (console browser, event analytics, log permintaan server), di mana token disalin dan disimpan.
Contoh konkret: seorang agen dukungan menempelkan "debug log" ke tiket. Log itu menyertakan header Authorization. Siapa pun dengan akses tiket bisa memutar ulang token itu dan bertindak sebagai agen. Perlakukan token seperti kata sandi: jangan mencetaknya, jangan menyimpannya, dan buat masa hidupnya singkat.
Pemeriksaan cepat sebelum peluncuran
Sebagian besar bug sesi bukan soal kripto canggih. Mereka soal satu flag yang hilang, satu token yang hidup terlalu lama, atau satu endpoint yang seharusnya memerlukan re-auth.
Sebelum rilis, lakukan pemeriksaan singkat yang berfokus pada apa yang bisa dilakukan penyerang dengan cookie atau token yang dicuri. Ini salah satu cara tercepat untuk meningkatkan keamanan tanpa menulis ulang seluruh setup auth Anda.
Daftar periksa pra-rilis
Jalankan pemeriksaan ini di staging, lalu lagi di produksi:
- Jaga access token berumur pendek (menit), dan pastikan API benar-benar menolaknya setelah kedaluwarsa.
- Perlakukan refresh token seperti kata sandi: simpan di tempat yang tidak bisa dibaca JavaScript bila memungkinkan, kirim hanya ke endpoint refresh, dan rotasi setelah setiap penggunaan.
- Jika Anda menggunakan cookie untuk auth, verifikasi flag: HttpOnly aktif, Secure aktif, dan SameSite diset dengan sengaja. Konfirmasi juga cakupan cookie (domain dan path) tidak lebih luas dari yang diperlukan.
- Jika cookie mengautentikasi permintaan, tambahkan pertahanan CSRF, dan pastikan endpoint yang mengubah state gagal tanpa sinyal CSRF.
- Buat pencabutan nyata: setelah reset kata sandi atau penonaktifan akun, sesi yang ada harus segera berhenti bekerja (hapus sesi sisi-server, cabut refresh token, atau cek "session version").
Setelah itu, uji janji logout Anda. "Log out" sering berarti "hapus sesi lokal," tetapi pengguna berharap lebih.
Tes praktis: masuk di laptop dan ponsel, lalu ganti kata sandi. Laptop harus dipaksa keluar pada permintaan berikutnya, bukan beberapa jam kemudian. Jika Anda menawarkan "log out everywhere" dan daftar perangkat, pastikan setiap perangkat dipetakan ke sesi terpisah atau record refresh token yang bisa dicabut.
Contoh: portal pelanggan dengan akun staf dan forced logout
Bayangkan usaha kecil dengan portal pelanggan web (pelanggan cek invoice, buka tiket) dan aplikasi mobile untuk staf lapangan (pekerjaan, catatan, foto). Staf kadang bekerja di area tanpa sinyal, jadi app harus tetap bekerja offline untuk sementara. Admin juga ingin tombol merah besar: jika tablet hilang atau kontraktor pergi, mereka bisa memaksa logout.
Tambahkan tiga ancaman umum: tablet bersama di van (seseorang lupa sign out), phishing (staf mengetik kredensial di halaman palsu), dan bug XSS sesekali di portal (skrip berjalan di browser dan mencoba mencuri apa pun yang bisa).
Setup praktis di sini adalah access token berumur pendek ditambah refresh token yang dirotasi, dengan pencabutan server-side. Ini memberi Anda panggilan API cepat dan toleransi offline, sambil tetap membiarkan admin memutus sesi.
Contoh konfigurasi:
- Masa hidup access token: 5 sampai 15 menit.
- Rotasi refresh token: setiap refresh mengeluarkan refresh token baru, dan yang lama menjadi tidak valid.
- Simpan refresh token dengan aman: di web, letakkan refresh token di cookie HttpOnly dan Secure; di mobile, simpan di secure storage OS.
- Lacak refresh token di sisi-server: simpan record token (user, perangkat, waktu diterbitkan, terakhir dipakai, flag revoked). Jika token yang telah dirotasi digunakan kembali, anggap itu pencurian dan cabut seluruh rantai.
Forced logout jadi dapat ditegakkan: admin mencabut record refresh token untuk perangkat itu (atau semua perangkat untuk pengguna). Perangkat yang dicuri masih bisa memakai access token saat ini sampai kedaluwarsa, tetapi tidak bisa mendapatkan yang baru. Jadi waktu maksimum untuk memutus akses adalah masa hidup access token Anda.
Untuk perangkat hilang, definisikan aturan dengan bahasa yang jelas: "Dalam 10 menit, app akan berhenti sinkron dan meminta sign-in lagi." Pekerjaan offline masih bisa tersimpan di perangkat, tetapi sinkronisasi berikutnya online harus gagal sampai pengguna masuk lagi.
Langkah selanjutnya: implementasi, uji, dan jaga agar mudah dipelihara
Tuliskan apa arti "logout" dalam bahasa produk yang sederhana. Misalnya: "Logout menghapus akses di perangkat ini," "Logout di mana saja mengeluarkan semua perangkat dalam 1 menit," atau "Mengganti kata sandi membuat sesi lain keluar." Janji-janji itu menentukan apakah Anda butuh state sesi sisi-server, daftar pencabutan, atau token singkat.
Ubah janji itu menjadi rencana uji kecil. Bug token dan sesi sering terlihat baik di demo jalur bahagia, lalu gagal di kehidupan nyata (mode tidur, jaringan buruk, banyak perangkat).
Daftar uji praktis
Jalankan tes yang menutupi kasus berantakan:
- Expiry: akses berhenti saat access token atau sesi kedaluwarsa, bahkan jika browser tetap terbuka.
- Revocation: setelah "logout everywhere," kredensial lama gagal pada permintaan berikutnya.
- Rotation: rotasi refresh token mengeluarkan refresh token baru dan menonaktifkan yang lama.
- Reuse detection: memutar ulang refresh token lama memicu respons penguncian.
- Multi-device: aturan untuk "hanya perangkat sekarang" vs "semua perangkat" ditegakkan dan UI sesuai.
Setelah tes, lakukan latihan serangan sederhana dengan tim Anda. Pilih tiga skenario dan jalankan dari awal sampai akhir: bug XSS yang bisa membaca token, upaya CSRF terhadap sesi cookie, dan ponsel dicuri dengan sesi aktif. Anda memeriksa apakah desain Anda cocok dengan janji Anda.
Jika perlu bergerak cepat, kurangi glue code kustom. AppMaster (appmaster.io) adalah salah satu opsi ketika Anda ingin backend siap produksi yang digenerasi plus web dan aplikasi mobile native, sehingga Anda bisa menjaga aturan seperti expiry, rotasi, dan forced logout konsisten antar klien.
Jadwalkan review lanjutan setelah peluncuran. Gunakan tiket dukungan dan insiden nyata untuk menyesuaikan timeout, batas sesi, dan perilaku "logout everywhere," lalu jalankan kembali daftar periksa yang sama supaya perbaikan tidak diam-diam mundur.


