TrendVidStream memindahkan seluruh tumpukan autentikasinya ke sistem rotating-refresh-token yang dapat mendeteksi token yang dicuri saat token tersebut digunakan kembali. Perubahan ini mengubah JWT berdurasi 30 hari yang sebelumnya membiarkan penyerang bergerak bebas menjadi kredensial berumur pendek yang secara instan memicu penguncian akun (lock-out).
Satu pelanggaran keamanan memaksa peralihan ini: sebuah SDK mitra menyimpan cache JWT 30 hari dalam bentuk teks biasa (plain text), seorang penyerang mengekstraknya dan menggunakannya kembali (replay) dari negara lain, dan satu-satunya solusi adalah dengan merotasi kunci penandatanganan (signing key)—sebuah operasi yang mengeluarkan semua pengguna dari sistem. Insiden tersebut membentuk kembali keamanan token perusahaan dan kini menjadi penggerak layanan video-streaming tersebut.
Mengapa model lama gagal
JWT (JSON Web Tokens) adalah blok data (blobs) bertanda tangan yang bersifat mandiri (self-contained), yang memungkinkan server memverifikasi permintaan tanpa perlu melakukan pencarian ke basis data. Kemudahan ini memiliki konsekuensi: jika sebuah token berlaku selama berminggu-minggu, mencurinya akan memberi penyerang akses selama berminggu-minggu pula. Dalam pelanggaran tersebut, token yang dicuri tetap valid hingga masa kedaluwarsanya yang 30 hari karena tidak ada cara untuk membatalkannya lebih awal.
Merotasi kunci penandatanganan adalah satu-satunya cara global untuk membatalkan semua token, tetapi hal ini memaksa setiap pengguna untuk masuk (log in) kembali, yang mengganggu layanan dan mengikis kepercayaan. Kelemahannya bukan terletak pada JWT itu sendiri, melainkan pada ketergantungan pada satu kredensial yang berumur panjang.
Ringkasan desain baru
TrendVidStream kini menerbitkan dua jenis token:
- Access tokens – berlaku selama 15 menit; token ini membawa izin yang diperlukan untuk setiap panggilan API.
- Refresh tokens – token sekali pakai yang menukarkan access token berumur pendek dengan pasangan token baru.
Saat klien menyajikan refresh token, server akan:
- Memverifikasi tanda tangan dan klaim (claims) token.
- Memeriksa apakah token tersebut sudah pernah digunakan.
- Jika pemeriksaan berhasil, menerbitkan refresh token yang benar-benar baru dan access token 15 menit yang baru.
- Menandai refresh token lama sebagai telah digunakan.
Jika langkah 2 gagal—artinya token yang sama muncul untuk kedua kalinya—server menganggapnya sebagai sinyal pencurian dan mencabut seluruh "keluarga" (family) token yang terkait dengan sesi login tersebut. Baik korban maupun penyerang dipaksa untuk masuk kembali, sehingga memutus akses pelanggaran tersebut dengan cepat.
Melacak keluarga token
Alih-alih memperlakukan setiap token sebagai catatan yang terisolasi, sistem mengelompokkannya ke dalam keluarga yang dimulai saat pengguna masuk (log in). Setiap rotasi menciptakan anggota baru dalam keluarga tersebut. Skema SQLite yang digunakan dalam produksi menyimpan:
- family_id – pengidentifikasi stabil untuk seluruh sesi.
- token_id – pengidentifikasi unik untuk setiap refresh token.
- generation – penghitung (counter) yang berguna untuk proses debugging.
- used_at – stempel waktu (timestamp) saat token ditukarkan.
- revoked – sebuah flag yang, jika diatur, akan menonaktifkan seluruh keluarga token.
Ketika peristiwa penggunaan kembali terdeteksi, flag revoked untuk family_id tersebut diatur, yang secara instan membatalkan setiap token yang termasuk dalam sesi yang terkompromi. Hal ini mengubah pembajakan senyap menjadi peringatan ber-sinyal tinggi yang muncul di dalam log.
Tiga aturan implementasi agar sistem tetap andal
Validasi tanda tangan terlebih dahulu Penyerang dapat menebak ID token dan memicu pencabutan massal jika server memeriksa "sudah digunakan sebelumnya?" sebelum memverifikasi tanda tangan. Mengonfirmasi autentisitas terlebih dahulu mencegah serangan denial-of-service yang tidak perlu.
Berikan jendela toleransi (grace window) Aplikasi seluler sering kali mengirimkan dua permintaan refresh secara berurutan saat sebuah token kedaluwarsa. Jika server terlalu ketat, permintaan kedua akan ditandai sebagai penggunaan kembali, sehingga mengeluarkan pengguna yang sah. Buffer selama beberapa detik memungkinkan token yang "sudah digunakan" tetap dapat mengembalikan pasangan token baru, guna mengatasi kondisi balapan (race conditions).
Bungkus seluruh alur dalam sebuah transaksi dengan penguncian baris (row locking) Tanpa atomisitas, dua permintaan bersamaan dapat sama-sama menganggap mereka adalah yang pertama menggunakan token, sehingga menerbitkan refresh token duplikat dan merusak jaminan penggunaan sekali pakai. Transaksi basis data yang mengunci baris token menjamin bahwa hanya satu permintaan yang berhasil.
Kesimpulan: Dengan menjadikan refresh token bersifat sekali pakai dan memantau penggunaan kembali, sebuah sistem dapat mengubah setiap kredensial yang dicuri menjadi alarm, melindungi pengguna tanpa harus memaksa keluar (logout) seluruh platform.
