Session membuat browser hanya memegang satu kunci: ID session yang acak. Masalahnya, kunci tersebut bersifat bearer — siapa pun yang dapat memperoleh ID, apa pun caranya, otomatis diperlakukan sebagai pemilik session. Jika penyerang berhasil menanam atau mengetahui sebuah ID sebelum korban login, penyerang mewarisi session korban tanpa perlu tahu passwordnya. session_regenerate_id() adalah alat PHP untuk memutus rantai tersebut: mengganti kunci lama dengan kunci baru pada momen ketika kepercayaan berubah. Chapter ini membedah serangan session fixation, cara kerja regenerasi, dan checklist pertahanan session yang lengkap.
ID Session Adalah Kunci Akses Mutlak #
Setelah login, server tidak lagi memeriksa password pada setiap request — cukup memercayai siapa pun yang menunjukkan ID session yang berisi user_id. Desain ini cepat, tetapi memindahkan seluruh beban keamanan ke kerahasiaan ID. ID yang bocor melalui tautan yang dibagikan, riwayat browser, skrip pihak ketiga, atau komputer bersama sama berbahayanya dengan password yang bocor. Karena itu ID session harus selalu acak, panjang, hanya berpindah lewat cookie berflag aman, dan diganti begitu tingkat kepercayaan berubah.
Anatomi Serangan Session Fixation #
Session fixation adalah serangan di mana penyerang memaksa korban memakai ID session yang sudah diketahui penyerang. Alurnya berjalan sebagai berikut:
- Penyerang membuka aplikasi dan mencatat ID session miliknya sendiri, atau menautkan URL berparameter ID session kepada korban, atau menyetel cookie dari subdomain yang ia kuasai.
- Penyerang mengirim tautan tersebut ke korban, misalnya lewat email berpura-pura promo.
- Korban mengklik tautan dan login menggunakan session ber-ID yang sudah diketahui penyerang.
- Setelah login, ID tersebut kini membawa status “sudah login sebagai korban” — penyerang tinggal memakai ID yang sama untuk masuk ke akun korban.
Serangan ini gagal total bila aplikasi mengganti ID pada saat login: kunci yang ditanam penyerang tidak lagi membuka session korban. Bedah lengkap varian serangan dan mitigasinya tersedia pada chapter Session Fixation di section Web Security.
Mengganti ID dengan session_regenerate_id() #
Fungsi session_regenerate_id() menerbitkan ID acak baru, memindahkan seluruh isi $_SESSION ke ID baru, dan menyiapkan cookie pengganti untuk browser. Contoh berikut membuktikan ketiga efek tersebut — ID berubah, data bertahan, dan file penyimpanan berpindah:
<?php
$dir = sys_get_temp_dir() . '/php-kursus-sesi';
if (!is_dir($dir)) {
mkdir($dir);
}
session_save_path($dir);
session_id('fixed-session-id-123');
@unlink($dir . '/sess_fixed-session-id-123');
session_start();
$_SESSION['user_id'] = 42;
$idLama = session_id();
session_regenerate_id(true);
$idBaru = session_id();
echo 'ID berubah: ' . var_export($idBaru !== $idLama, true) . "\n";
echo 'Data bertahan: ' . var_export($_SESSION['user_id'] === 42, true) . "\n";
echo 'File lama terhapus: ' . var_export(!file_exists($dir . '/sess_' . $idLama), true) . "\n";
echo 'File baru dibuat: ' . var_export(file_exists($dir . '/sess_' . $idBaru), true) . "\n";
echo 'Panjang ID baru: ' . strlen($idBaru) . " karakter\n";Output yang Dihasilkan:
ID berubah: true
Data bertahan: true
File lama terhapus: true
File baru dibuat: true
Panjang ID baru: 32 karakterMari kita telaah hasil eksekusinya:
- Baris 18 (
session_regenerate_id(true)): PHP membuat ID acak baru, menyalin data ke penyimpanan baru, lalu menghapus yang lama karena argumentrue. - ID berubah dan data bertahan:
$_SESSION['user_id']tetap bernilai42— regenerasi tidak mengusir pengguna yang sedang login. - File berpindah: file
sess_fixed-session-id-123lenyap dan digantikan file ber-ID acak, sehingga kunci lama tidak lagi membuka apa pun.
Parameter delete_old_session #
Argumen delete_old_session menentukan nasib data di bawah ID lama. Nilai false (bawaan) membiarkan file lama tetap ada di server; nilai true menghapusnya segera. Perbandingan berikut menunjukkan efek false:
<?php
$dir = sys_get_temp_dir() . '/php-kursus-sesi';
if (!is_dir($dir)) {
mkdir($dir);
}
session_save_path($dir);
session_id('fixed-session-id-123');
@unlink($dir . '/sess_fixed-session-id-123');
session_start();
$_SESSION['user_id'] = 42;
$idLama = session_id();
session_regenerate_id(false); // data lama tidak dihapus
$idBaru = session_id();
echo 'ID berubah: ' . var_export($idBaru !== $idLama, true) . "\n";
echo 'File lama masih ada: ' . var_export(file_exists($dir . '/sess_' . $idLama), true) . "\n";
echo 'File baru dibuat: ' . var_export(file_exists($dir . '/sess_' . $idBaru), true) . "\n";
@unlink($dir . '/sess_' . $idLama);
@unlink($dir . '/sess_' . $idBaru);Output yang Dihasilkan:
ID berubah: true
File lama masih ada: true
File baru dibuat: trueDengan false, ID lama tetap valid di server selama belum menyentuh gc_maxlifetime — jendela kecil yang bisa dipakai penyerang bila ID lama sempat bocor. Untuk momen login dan kenaikan hak akses, selalu pakai true. Nilai false hanya relevan pada sistem yang memakai request paralel, misalnya beberapa panggilan AJAX yang sedang berjalan saat ID diganti: penghapusan langsung dapat membuat request lama kehilangan session. Pada kasus seperti itu, pertimbangkan mekanisme penundaan penghapusan di sisi aplikasi daripada membiarkan ID lama hidup lama.
Kapan ID Harus Diganti #
Prinsipnya satu: regenerasi dilakukan setiap kali tingkat kepercayaan naik atau identitas berganti. Tabel berikut merangkum momen-momen penting:
| Momen | Aksi | Alasan |
|---|---|---|
| Setelah login sukses | session_regenerate_id(true) | memutus ID yang mungkin sudah ditanam penyerang sebelum login |
| Kenaikan hak akses (mis. jadi admin) | session_regenerate_id(true) | hak baru tidak boleh menempel pada ID lama yang mungkin bocor |
| Berkala (mis. tiap 30 menit) | session_regenerate_id(true) | memperpendek umur efektif sebuah ID bila bocor |
| Setelah logout | session_unset() + session_destroy() | session sudah tidak dipakai; hapus, bukan ganti |
| Sebelum login (pre-session) | cukup strict mode | ID anonim tidak bernilai bagi penyerang |
Pola Login dan Logout yang Lengkap #
Berikut pola login yang menyatukan seluruh pertahanan yang telah kita bahas. Pastikan blok ini berjalan sebelum output apa pun, misalnya di paling awal skrip atau controller:
<?php
// Pola login: selalu regenerate ID setelah verifikasi kredensial
session_start();
$loginBerhasil = true; // aplikasi nyata: password_verify() terhadap hash
if ($loginBerhasil) {
// Ganti ID segera setelah kepercayaan naik
session_regenerate_id(true);
// Simpan identitas minimal, bukan data sensitif
$_SESSION['user_id'] = 42;
$_SESSION['login_pada'] = time();
}Urutannya penting: regenerasi dilakukan sebelum identitas ditulis, sehingga data login hanya menempel pada ID baru. Untuk regenerasi berkala, simpan cap waktu terakhir regenerasi di dalam session itu sendiri:
<?php
// Regenerasi berkala: batasi umur efektif sebuah ID
$batasDetik = 30 * 60;
if (!isset($_SESSION['regenerasi_pada'])) {
$_SESSION['regenerasi_pada'] = time();
} elseif (time() - $_SESSION['regenerasi_pada'] > $batasDetik) {
session_regenerate_id(true);
$_SESSION['regenerasi_pada'] = time();
}Logout yang tuntas membersihkan tiga lapis sekaligus: array di memori, data di server, dan cookie di browser:
<?php
// Pola logout: kosongkan data, hapus cookie, hancurkan session
session_start();
$_SESSION = [];
// Hapus juga cookie PHPSESSID di sisi browser
if (ini_get('session.use_cookies')) {
$params = session_get_cookie_params();
setcookie(
session_name(),
'',
1,
$params['path'],
$params['domain'],
$params['secure'],
$params['httponly'],
);
}
session_destroy();Pemanggilan session_get_cookie_params() mengambil path, domain, dan flag keamanan cookie session yang sedang aktif, sehingga cookie penghapusan ditulis dengan atribut yang persis sama — browser hanya menimpa cookie bila nama, path, dan domainnya cocok. Cookie dengan kedaluwarsa 1 (masa lalu) membuat browser membuang PHPSESSID seketika.
Checklist Pertahanan Session #
Rangkaian lima chapter sejak Cookies bermuara pada daftar berikut. Jadikan checklist ini bagian dari review sebelum rilis:
- Aktifkan
session.use_strict_mode = 1agar ID buatan penyerang selalu ditolak. - Panggil
session_regenerate_id(true)segera setelah login sukses dan setiap kenaikan hak akses. - Setel flag cookie session —
secure,httponly,samesite— baik lewat php.ini maupunini_set()sebelumsession_start(). - Simpan hanya identitas minimal (
user_id) di$_SESSION, jangan pernah password atau data sensitif lain. - Saat logout, jalankan pola tuntas: kosongkan array, hapus cookie dengan atribut yang sama, lalu
session_destroy(). - Sesuaikan
session.gc_maxlifetimedengan kebutuhan, dan ingat bahwa penghapusan bersifat probabilistik.
Ringkasan #
ID session adalah kunci akses mutlak, sehingga harus diterbitkan ulang setiap kali kepercayaan berubah: session_regenerate_id(true) mengganti ID, memindahkan data, dan menghapus kunci lama sekaligus. Regenerasi dipasang setelah login dan kenaikan hak, dijalankan berkala untuk memperpendek umur ID, dan berpasangan dengan strict mode serta flag cookie aman. Dengan checklist enam poin di atas, session kita tahan terhadap fixation, pencurian, dan penyalahgunaan lintas situs. Setelah identitas pengguna aman, kebutuhan berikutnya muncul sendiri: memberi tahu pengguna bahwa aksi terakhirnya berhasil — pesan sekali pakai yang dikenal sebagai flash messages.