Setelah pengguna berhasil login, banyak aplikasi PHP langsung menyimpan user_id ke dalam $_SESSION. Kelihatannya sederhana, tetapi di titik inilah identitas pengguna mulai “menempel” pada browser. Jika session ID tidak diganti, cookie tidak dilindungi, atau logout hanya menghapus tampilan di browser, akun bisa tetap berisiko meskipun password-nya kuat.
Session adalah mekanisme untuk mengingat bahwa beberapa permintaan HTTP berasal dari pengguna yang sama. Browser biasanya menyimpan session ID dalam cookie, sementara data sebenarnya disimpan di server. Karena session ID berfungsi seperti tiket masuk, siapa pun yang memilikinya berpotensi memakai akun tersebut selama tiket itu masih berlaku.
Masalahnya bukan hanya session ID yang dicuri
Serangan yang sering dibahas adalah session hijacking, yaitu ketika penyerang mendapatkan session ID milik korban. Namun ada skenario lain yang tidak kalah penting: session fixation. Dalam serangan ini, penyerang mencoba membuat korban memakai session ID yang sudah diketahui penyerang. Jika aplikasi tetap memakai ID yang sama setelah login, penyerang dapat menggunakan ID tersebut untuk masuk sebagai korban.
OWASP merekomendasikan agar session ID diganti ketika status pengguna berubah, terutama setelah proses autentikasi berhasil. PHP juga mendokumentasikan bahwa session_regenerate_id() sebaiknya dipanggil sebelum informasi autentikasi ditulis ke dalam $_SESSION. Penjelasan lengkapnya tersedia di OWASP Session Management Cheat Sheet dan dokumentasi keamanan session PHP.
Empat lapisan perlindungan yang perlu diperiksa
1. Ganti session ID setelah login
Jangan mempertahankan session ID anonim menjadi session ID akun yang sudah terautentikasi. Setelah username dan password diverifikasi, buat ID baru terlebih dahulu, lalu simpan status login.
<?php
session_start();
if ($passwordValid) {
session_regenerate_id(false);
$_SESSION['user_id'] = $user['id'];
$_SESSION['authenticated_at'] = time();
$_SESSION['last_activity'] = time();
}
?>Contoh tersebut sengaja memakai false agar session lama tidak langsung dihapus secara agresif. Dokumentasi PHP mengingatkan bahwa penghapusan session lama secara langsung dapat menimbulkan masalah pada koneksi yang tidak stabil atau permintaan yang berjalan bersamaan. Dalam aplikasi produksi, tandai session lama sebagai usang dan pastikan session usang tidak lagi diterima.
Session ID juga sebaiknya diganti ketika pengguna menaikkan hak akses, misalnya setelah memasukkan ulang password sebelum mengubah email, mengaktifkan fitur administrator, atau menyelesaikan autentikasi dua faktor.
2. Atur cookie dengan atribut keamanan
Cookie session sebaiknya tidak dibiarkan memakai pengaturan bawaan tanpa pemeriksaan. Tiga atribut penting adalah:
- Secure: cookie hanya dikirim melalui HTTPS.
- HttpOnly: JavaScript di halaman tidak dapat membaca cookie melalui
document.cookie. Ini membantu mengurangi dampak pencurian cookie melalui XSS. - SameSite: membatasi pengiriman cookie pada permintaan lintas situs dan membantu mengurangi risiko CSRF.
Konfigurasi dasar di PHP dapat terlihat seperti ini:
<?php
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax'
]);
session_start();
?>Gunakan SameSite=Strict jika alur aplikasi memungkinkan. Namun, Lax sering menjadi pilihan yang lebih kompatibel untuk website yang masih membutuhkan navigasi lintas situs tertentu. Atribut ini bukan pengganti token CSRF, melainkan lapisan tambahan.
3. Jangan memasukkan data sensitif ke session ID
Session ID seharusnya hanya menjadi pengenal acak, bukan wadah informasi. Jangan membuat format seperti user=andi|role=admin|expires=... lalu mengirimkannya ke browser. Informasi semacam itu dapat terbaca, dimanipulasi, atau bocor melalui log.
Simpan data penting di server, seperti user ID, peran, waktu login, dan waktu aktivitas terakhir. Gunakan generator session bawaan PHP atau framework yang sudah dirancang untuk menghasilkan nilai acak. OWASP merekomendasikan session ID yang tidak dapat ditebak dan tidak mengandung informasi pribadi.
4. Terapkan timeout dari sisi server
Menghapus cookie ketika pengguna menekan tombol logout belum tentu mengakhiri session yang sudah dicuri. Salinan cookie yang berada di perangkat lain masih dapat digunakan jika server belum membatalkan session tersebut.
Karena itu, simpan waktu aktivitas terakhir dan periksa pada setiap permintaan ke halaman yang membutuhkan login.
<?php
$timeout = 1800; // 30 menit
if (!isset($_SESSION['user_id'])) {
http_response_code(401);
exit('Silakan login.');
}
if (time() - ($_SESSION['last_activity'] ?? 0) > $timeout) {
$_SESSION = [];
session_destroy();
http_response_code(401);
exit('Sesi sudah berakhir.');
}
$_SESSION['last_activity'] = time();
?>Untuk aktivitas berisiko tinggi, gunakan timeout yang lebih pendek atau minta pengguna memasukkan password lagi. Jangan mengandalkan session.gc_maxlifetime sebagai satu-satunya mekanisme kedaluwarsa, karena pengaturan garbage collection bukan pengganti pemeriksaan status session di level aplikasi.
Bagaimana dengan IP address dan User-Agent?
Mengikat session ke IP address atau User-Agent dapat membantu mendeteksi perubahan yang mencurigakan, tetapi jangan langsung memblokir pengguna setiap kali nilainya berubah. Pengguna ponsel dapat berpindah jaringan, melewati proxy, atau memakai koneksi yang alamat IP-nya berubah.
Pendekatan yang lebih aman adalah menjadikan perubahan tersebut sebagai sinyal risiko. Misalnya, minta autentikasi ulang jika session tiba-tiba berpindah negara, User-Agent berubah drastis, atau ada akses administrator dari perangkat baru. Ini adalah keputusan desain dan analisis risiko, bukan aturan yang cocok untuk semua website.
Checklist yang bisa dilakukan sekarang
- Pastikan website selalu menggunakan HTTPS dan cookie session memiliki atribut
Secure. - Aktifkan
HttpOnlydan pilihSameSite=LaxatauStrictsesuai kebutuhan alur website. - Panggil
session_regenerate_id()setelah login dan sebelum menyimpan status autentikasi. - Batasi durasi idle session dari sisi server.
- Batalkan session di server saat logout atau ketika password dan hak akses berubah.
- Jangan menerima session ID dari URL atau parameter GET.
- Catat kejadian penting seperti login baru, logout, perubahan password, dan session yang kedaluwarsa.
Apa artinya bagi kita?
Session security bukan fitur tambahan yang hanya diperlukan website besar. Blog WordPress dengan area admin, toko online kecil, dan aplikasi internal PHP semuanya menggunakan konsep yang sama: browser membawa tanda pengenal, lalu server memutuskan apakah tanda itu masih sah.
Perbaikan paling penting biasanya bukan menulis sistem autentikasi dari nol, melainkan memastikan alurnya benar: gunakan session ID yang acak, ganti ID setelah login, lindungi cookie, batasi umur session, dan validasi hak akses pada setiap tindakan. Password yang kuat membantu membuka pintu, tetapi pengelolaan session menentukan apakah pintu itu tetap terkunci setelah pengguna masuk.
Sumber & bacaan lebih lanjut
- OWASP Session Management Cheat Sheet
- PHP Manual: Session Management Basics
- PHP Manual: session_regenerate_id
- OWASP Web Security Testing Guide: Session Fixation
– Rio Yotto @rioyotto
