Rotasi ID Session Menutup Celah Session Pra-Autentikasi
Aplikasi web dapat membuat session sebelum user masuk. Session anonim itu mungkin menyimpan token CSRF, locale, isi keranjang, atau state sementara lain. Autentikasi mengubah otoritas yang melekat pada session: server kini memperlakukan request yang membawa session tersebut sebagai milik akun tertentu.
Jika aplikasi mempertahankan identifier session yang sama sepanjang transisi itu, nilai yang sudah ada sebelum autentikasi dapat berubah menjadi pegangan untuk session terautentikasi. Serangan session fixation memanfaatkan kesinambungan tersebut. Batas pertahanannya berada tepat pada peristiwa autentikasi: pertahankan hanya state yang memang perlu dibawa, terbitkan identifier baru yang tidak dapat diprediksi, lalu hentikan identifier lama.
Autentikasi mengubah arti sebuah session
Identifier session biasanya berupa lookup key yang opaque. Server memetakannya ke state di memory, database, distributed cache, atau session store lain. Sebelum autentikasi, state tersebut memiliki otoritas anonim. Setelah autentikasi berhasil, state dapat membawa identitas akun dan konteks otorisasi.
Perubahan pentingnya bukan jumlah data di dalam session, melainkan privilege yang direpresentasikan oleh kepemilikan identifier tersebut.
Perhatikan alur sederhana berikut:
GET /login
Set-Cookie: sid=A
POST /login
Cookie: sid=A
autentikasi berhasil
GET /account
Cookie: sid=AJika A tetap valid dan sekarang menunjuk ke akun yang sudah terautentikasi, aplikasi telah memakai kembali kredensial pra-autentikasi melewati batas privilege.
Penyerang tidak perlu menebak identifier baru pasca-login jika aplikasi menerima identifier yang sudah ditetapkan sebelumnya. Serangan bergantung pada kemampuan menempatkan identifier session yang diketahui atau dipilih ke browser korban, lalu menunggu korban melakukan autentikasi pada session yang sama.
Rotasi membuat batas kepemilikan baru
Setelah autentikasi berhasil, server perlu membuat identifier session baru dengan mekanisme pembangkitan session yang aman secara kriptografis sebagaimana digunakan aplikasi. State terautentikasi dikaitkan dengan identifier baru, sedangkan identifier sebelumnya dibuat tidak valid.
Transisinya dapat digambarkan sebagai berikut:
sebelum login:
sid=A -> state anonim
setelah login:
sid=A -> tidak valid
sid=B -> state terautentikasiBrowser menerima B hanya setelah tahap autentikasi. Pengetahuan atas A tidak lagi memberi akses ke session terautentikasi yang dihasilkan.
Rotasi juga relevan pada perubahan privilege lain yang berarti. Contohnya adalah peningkatan ke mode administratif, selesainya step-up authentication, atau transisi yang secara material memperluas otoritas session. Batas tepatnya mengikuti model otorisasi aplikasi, tetapi prinsipnya tetap sama: kredensial yang dibuat pada otoritas lebih rendah tidak seharusnya tetap identik setelah otoritas lebih tinggi diberikan.
Salin state secara selektif
Rotasi identifier tidak berarti seluruh state anonim harus dibuang. Aplikasi dapat tetap membawa locale, keranjang, atau tujuan kembali setelah login. Pemindahan tersebut sebaiknya disengaja, bukan meneruskan seluruh objek session lama begitu saja.
State yang berkaitan dengan autentikasi perlu perhatian khusus. Challenge login sementara, nilai anti-CSRF, OAuth correlation state, dan penanda workflow sekali pakai dapat memiliki siklus hidup yang terikat pada fase tertentu dalam proses autentikasi. Menyalinnya tanpa peninjauan dapat memperpanjang state melewati batas yang semestinya.
Model implementasi yang berguna memisahkan migrasi dari identitas:
old = load_session(A)
new = create_session()
new.locale = old.locale
new.cart = old.cart
new.user_id = authenticated_user.id
invalidate(A)
set_cookie(new.id)Framework nyata sering menyediakan primitive untuk regenerasi session, sehingga kode aplikasi mungkin tidak perlu menjalankan operasi tersebut secara manual. Persyaratan keamanannya terletak pada hasil akhir: identifier baru aktif setelah autentikasi dan identifier lama tidak lagi dapat mengotorisasi request.
Atribut cookie bekerja pada lapisan berbeda
Secure, HttpOnly, dan SameSite merupakan kontrol penting untuk cookie session, tetapi tidak ada yang menggantikan rotasi identifier.
Secure membatasi pengiriman cookie ke konteks transport yang aman. HttpOnly membatasi akses dari API script. SameSite memengaruhi penyertaan cookie pada request lintas situs tertentu. Session fixation berkaitan dengan kelanjutan validitas identifier melewati perubahan otoritas. Cookie dapat memiliki atribut yang kuat tetapi tetap membawa identifier yang sama sebelum dan sesudah login.
Scope cookie juga penting. Cookie session sebaiknya memakai domain dan path sesempit yang praktis bagi aplikasi. Scope yang terlalu luas dapat mengekspos kredensial ke aplikasi atau host saudara yang tidak memerlukannya. Ini merupakan kontrol terpisah dari rotasi, tetapi keduanya mengurangi jalur yang memungkinkan kredensial session melewati batas yang tidak dituju.
Invalidasi harus benar-benar berlaku
Membuat B belum cukup jika A tetap dapat dipakai sebagai alias untuk state terautentikasi yang sama. Identifier lama harus berhenti memberikan otorisasi terhadap session tersebut.
Detail ini dapat menjadi rumit pada sistem terdistribusi. State session mungkin di-cache pada beberapa node, direplikasi antarpencadangan, atau direpresentasikan sebagai struktur bertanda tangan di sisi client. Implementasi harus sesuai dengan arsitektur session. Store di sisi server dapat menghapus atau mencabut mapping lama. Desain lain mungkin memerlukan version, revocation marker, atau mekanisme lain agar kredensial sebelumnya tidak mempertahankan otoritas terautentikasi.
Perilaku saat race juga perlu ditetapkan secara eksplisit. Request yang membawa identifier lama dapat masih berada dalam perjalanan ketika login selesai. Transisi harus dirancang agar penerimaan request stale tidak memulihkan atau memperpanjang session yang sudah dihentikan.
Rotasi bukan seluruh kebijakan session
Identifier baru saat login menangani satu batas tertentu. Desain session yang baik juga memerlukan identifier yang tidak dapat diprediksi, transport terlindungi, lifetime terbatas, invalidasi saat logout, expiration idle atau absolut yang sesuai, serta kontrol untuk peristiwa keamanan akun.
Sistem autentikasi juga perlu menetapkan nasib session yang sudah ada setelah perubahan password, pemulihan akun, atau tindakan sensitif lain. Kebijakan tersebut menangani persistensi session yang sudah terautentikasi, bukan fixation terhadap identifier pra-autentikasi.
Pada aplikasi yang menyimpan state autentikasi sepenuhnya dalam bearer token, mekanismenya dapat berbeda dari session tradisional di sisi server. Pertanyaan batasnya tetap sama: apakah kredensial yang sudah ada sebelum autentikasi dapat mempertahankan atau memperoleh otoritas terautentikasi tanpa diganti oleh kredensial yang diterbitkan setelah autentikasi berhasil?
Uji transisinya, bukan hanya hasil login
Regression test dapat memeriksa properti ini secara langsung. Mulai session anonim dan catat identifier-nya. Lakukan autentikasi hingga berhasil. Pastikan response menetapkan identifier yang berbeda, lalu kirim request memakai identifier lama dan verifikasi bahwa request tersebut tidak memperoleh akses terautentikasi.
Uji juga autentikasi yang gagal. Login gagal tidak boleh memberikan otoritas kepada session lama maupun session baru. Jika aplikasi merotasi identifier pada percobaan gagal sebagai hardening tambahan, test perlu mengikuti kebijakan tersebut tanpa memperlakukan identifier anonim baru sebagai terautentikasi.
Untuk deployment terdistribusi, jalankan pemeriksaan identifier lama melalui node aplikasi yang berbeda jika memungkinkan. Langkah ini dapat menangkap celah invalidasi yang tersembunyi pada test lokal satu node.
Properti penentunya kecil dan dapat diamati: identifier yang ada sebelum autentikasi tidak boleh tetap menjadi kredensial valid bagi session terautentikasi. Transisi yang eksplisit mencegah state session anonim berubah diam-diam menjadi bearer credential dengan otoritas akun.