Browser punya tiga tempat utama untuk menyimpan data: localStorage, sessionStorage, dan cookie. Bedanya ada di tiga hal: siapa yang bisa membacanya, kapan datanya hilang, dan apakah datanya ikut terkirim ke server. Untuk keamanan, pertanyaan terpentingnya: kalau ada script jahat di halamanmu (XSS), apa yang bisa dia baca? Bayangin kantor dengan tiga tempat menaruh kunci: laci meja yang gak dikunci, papan tulis yang dihapus tiap kali ruangan dikosongkan, dan brankas yang cuma bisa dibuka satpam. Penyusup yang masuk ruangan bisa membuka laci dan membaca papan tulis, tapi gak bisa membuka brankas. Lacinya localStorage, papan tulisnya sessionStorage, brankasnya cookie HttpOnly, dan satpamnya browser.
🧪 Tebakan cepat: token login disimpan di cookie dengan
SecuredanSameSite=Strict, tapi tanpaHttpOnly. Script XSS di halamanmu bisa membacanya lewatdocument.cookieatau gak? Jawabannya ada di bagian "Cookie: cuma HttpOnly yang menyembunyikan dari script".
Tiga tempat, tiga sifat
localStorage |
sessionStorage |
Cookie biasa | Cookie HttpOnly |
|
|---|---|---|---|---|
| Dibaca JavaScript halaman | bisa | bisa | bisa, lewat document.cookie |
gak bisa |
| Ikut terkirim ke server | gak | gak | ya, otomatis | ya, otomatis |
| Hilang saat | dihapus kode atau user | tab ditutup | kedaluwarsa | kedaluwarsa |
| Kapasitas | sekitar 5 MB per origin | sekitar 5 MB per origin | sekitar 4 KB per cookie | sekitar 4 KB per cookie |
localStorage.setItem("theme", "dark");
localStorage.getItem("theme"); // "dark", besok pun masih ada
sessionStorage.setItem("draft", "Halo, pesanan saya...");
// tutup tab: draft hilang
document.cookie; // "theme=dark" (cookie HttpOnly gak pernah muncul di sini)
localStoragebertahan walau tab dan browser ditutup, sampai dihapus denganlocalStorage.removeItematau oleh user.sessionStoragehidup selama tab itu terbuka. Reload masih aman, tapi tab ditutup berarti datanya hilang.- Keduanya terpisah per origin:
https://myapp.comgak bisa membaca storage milikhttps://evil.com, dan sebaliknya.
Apa yang dibaca script XSS
Di Lab, Budi login di https://myapp.com, dan server mengembalikan JWT eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOjQyfQ.c2lnbmF0dXJl. Aplikasinya menyimpan token itu di localStorage. Lalu ada komentar berisi script sisipan yang lolos ke halaman. Script itu jalan dengan hak yang sama dengan kodemu sendiri, jadi dia tinggal bertanya:
> localStorage.getItem("token")
'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOjQyfQ.c2lnbmF0dXJl'
> document.cookie
'theme=dark'
Satu baris lagi, dan token itu terkirim ke server penyerang. Dengan token itu, penyerang bisa login sebagai Budi dari komputernya sendiri sampai token-nya kedaluwarsa. Karena itu JWT gak boleh disimpan di localStorage: tempat itu terbuka untuk semua JavaScript di halaman, termasuk script XSS. sessionStorage sama terbukanya, bedanya cuma lebih cepat hilang.
Cookie: cuma HttpOnly yang menyembunyikan dari script
Tempat paling aman untuk token login adalah cookie HttpOnly. Server yang memasangnya lewat header response:
Set-Cookie: token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOjQyfQ.c2lnbmF0dXJl; Max-Age=7200; Path=/; HttpOnly; Secure; SameSite=Lax
HttpOnly: cookie ini gak muncul didocument.cookie. Ini satu-satunya flag yang menyembunyikan cookie dari JavaScript.Secure: cookie cuma dikirim lewat HTTPS. Di halaman HTTPS, JavaScript tetap bisa membacanya.SameSite: mengatur kapan cookie ikut di request dari situs lain (ingat pelajaran CSRF). Gak ada hubungannya dengan JavaScript membaca cookie.
Jadi cookie dengan Secure dan SameSite=Strict tapi tanpa HttpOnly tetap terbaca lewat document.cookie. Itu jawaban tebakannya.
Kodemu juga gak perlu menyentuh token-nya. Browser menempelkan cookie sendiri di setiap request ke myapp.com:
fetch("/api/orders", { credentials: "include" });
Satu hal lagi: cookie HttpOnly cuma bisa dibuat server. Kalau JavaScript menulis document.cookie = "token=abc; HttpOnly", browser mengabaikan cookie itu sepenuhnya.
Yang gak dicegah HttpOnly
HttpOnly mencegah token dicuri, bukan dipakai. Selama halaman yang kena XSS masih terbuka, script sisipan bisa memanggil fetch("/api/orders"), dan browser tetap menempelkan cookie-nya. Bedanya besar: penyerang gak bisa membawa token itu pulang dan memakainya dari tempat lain setelah Budi menutup tab.
Jadi HttpOnly mengurangi kerusakan, tapi obat utamanya tetap mencegah XSS: escaping output dan CSP, yang sudah kamu pelajari.
Simpan di mana?
- Token login atau session: cookie
HttpOnly; Secure; SameSite=Lax(atauStrict). - Preferensi seperti tema dan bahasa:
localStorage. Gak rahasia, dan enak kalau bertahan. - Draft form sementara:
sessionStorage, hilang sendiri saat tab ditutup. - Data sensitif lain seperti nomor kartu atau KTP: jangan disimpan di browser. Ambil dari server saat dibutuhkan.
Saat logout, bersihkan juga: hapus item di storage dengan removeItem, dan server mengirim ulang cookie-nya dengan Max-Age=0 supaya browser membuangnya.
💡 Bagaimana kalau token di localStorage dienkripsi?
Kedengarannya aman, tapi gak menolong melawan XSS. Untuk mendekripsi token, kodemu butuh kuncinya, dan kunci itu juga ada di halaman: di variabel, di storage, atau sebagai CryptoKey di IndexedDB. Script sisipan jalan dengan hak yang sama dengan kodemu, jadi dia bisa memakai kunci yang sama untuk mendekripsi, atau langsung memakai token-nya. Enkripsi di browser cuma menolong kalau seseorang membaca file browser di disk, bukan saat XSS.
⚠️ Jebakan yang sering ketemu
- Token di
localStoragekarena praktis. Satu celah XSS sudah cukup untuk menyalinnya, dan token itu tetap berlaku di tempat lain. - Mengira
SecureatauSameSitemenyembunyikan cookie dari script. CumaHttpOnlyyang melakukannya. - Mengira
sessionStoragelebih aman dari XSS. Sama terbukanya, cuma lebih cepat hilang. - Lupa membersihkan saat logout. Token yang tertinggal di
localStoragemasih bisa dibaca orang berikutnya yang memakai browser itu.
TL;DR: localStorage bertahan walau browser ditutup, sessionStorage hilang saat tab ditutup, dan keduanya bisa dibaca semua JavaScript di halaman, termasuk script XSS. Karena itu JWT atau token login gak boleh disimpan di sana: rentan XSS, penyerang tinggal menyalinnya. Tempat paling aman untuk token adalah cookie HttpOnly dengan Secure dan SameSite, yang dipasang server lewat Set-Cookie. HttpOnly satu-satunya flag yang menyembunyikan cookie dari document.cookie; Secure dan SameSite gak. HttpOnly mencegah token dicuri, bukan dipakai selama halaman yang kena XSS terbuka, jadi tetap cegah XSS-nya. Simpan preferensi di localStorage, draft di sessionStorage, dan data sensitif jangan di browser.