Keamanan Session

Session fixation, regenerasi ID, dan cookie aman.

Daftar isi

Session menyimpan status login dan data pengunjung di server, sehingga keamanannya menentukan keamanan seluruh aplikasi. Sasaran serangan pada bagian ini adalah session ID, yaitu pengenal yang dibawa cookie dan menjadi satu-satunya penghubung browser dengan data session di server. Topik ini membahas session fixation, regenerasi ID pada titik-titik penting, penyetelan cookie session, dan cara memutus session dengan benar.

Session ID sebagai kredensial#

PHP mengidentifikasi pengunjung melalui session ID yang dikirim browser pada setiap permintaan, sedangkan datanya tersimpan di server dan diisi kembali oleh session_start. Siapa pun yang membawa session ID tertentu dianggap pemilik datanya, sehingga session ID bekerja seperti kredensial. Berbeda dengan kata sandi, session ID berpindah pada setiap permintaan melalui header cookie dan dapat bocor melalui celah lain, misalnya skrip sisipan dari serangan XSS yang membacanya.

Dua pola serangan utama berkaitan erat. Session hijacking adalah pencurian session ID milik pengguna yang sudah login, lalu ID itu dipakai penyerang untuk masuk dengan identitas korban. Session fixation bekerja sebaliknya, yaitu menanamkan session ID pilihan penyerang ke browser korban sebelum korban login, misalnya melalui tautan yang membawa parameter ID. Setelah korban login dengan ID tersebut, penyerang yang mengetahuinya ikut masuk tanpa perlu kata sandi.

Kedua pola itu ditangani dengan kombinasi regenerasi ID, penyetelan cookie yang ketat, dan pembersihan session yang benar, sebagaimana dibahas pada bagian-bagian berikut.

Mencegah session fixation dengan regenerasi ID#

Fungsi session_regenerate_id(true) meminta PHP membuat session ID baru dan menghapus penyimpanan sesi lama, sehingga ID yang sudah diketahui pihak lain kehilangan nilai. Pemanggilannya wajib dilakukan pada setiap perubahan hak akses, terutama segera setelah proses login berhasil dan setelah penggantian kata sandi.

php
<?php
session_start();

$idLama = session_id();
session_regenerate_id(true);
$idBaru = session_id();

var_dump($idLama !== $idBaru);

session_write_close();
text Keluaran
bool(true)

Keluaran menegaskan bahwa session ID sebelum dan sesudah panggilan berbeda, dan argumen true menghapus penyimpanan sesi lama sehingga ID lama tidak merujuk data apa pun. Pada konteks web, cookie dengan ID baru dikirim ke browser, sehingga ID yang ditanam penyerang sebelum login menjadi tidak berguna. Regenerasi tidak mengosongkan $_SESSION, sehingga data login yang baru ditulis tetap tersimpan di bawah ID yang baru.

Frekuensi regenerasinya perlu seimbang. Pemanggilan sekali sesudah login dan pada setiap kenaikan hak akses sudah menutup pola fixation, sedangkan regenerasi pada setiap permintaan menambah keamanan tetapi dapat membebani penyimpanan sesi yang penuh berkas lama.

Cookie session disetel melalui session_set_cookie_params sebelum session_start, dan setiap opsi menutup satu celah serangan.

OpsiNilai yang disarankanCelah yang ditutup
httponlytrueSkrip di browser tidak dapat membaca cookie
securetrueCookie tidak terkirim melalui HTTP biasa
samesiteStrict atau LaxCookie tidak terkirim dari konteks lintas situs
lifetime0Cookie berakhir saat browser ditutup
php
<?php
session_set_cookie_params([
    'lifetime' => 0,
    'path' => '/',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Strict',
]);
$params = session_get_cookie_params();
echo 'lifetime : ' . $params['lifetime'] . PHP_EOL;
echo 'secure   : ' . var_export($params['secure'], true) . PHP_EOL;
echo 'httponly : ' . var_export($params['httponly'], true) . PHP_EOL;
echo 'samesite : ' . $params['samesite'] . PHP_EOL;
text Keluaran
lifetime : 0
secure   : true
httponly : true
samesite : Strict

Keluaran menegaskan bahwa seluruh opsi terbaca kembali melalui session_get_cookie_params, sehingga pengaturannya dapat diperiksa sebelum sesi dibuka. Nilai secure menuntut HTTPS, sehingga pada pengembangan lokal melalui HTTP biasa cookie tersebut tidak terkirim dan opsi itu diaktifkan pada penerapan produksi. Nilai lifetime nol membuat cookie berakhir bersama sesi browser. Masa berlaku data di sisi server diatur sendiri, misalnya dengan menyimpan waktu aktivitas terakhir di $_SESSION dan menghapus sesi ketika melewati batas yang ditetapkan.

Memutus session dengan benar#

Proses keluar yang benar mengosongkan $_SESSION dan menghapus data di penyimpanan server melalui session_destroy. Keduanya perlu dijalankan karena fungsi tersebut tidak mengosongkan variabel di memori skrip.

php
<?php
session_start();
$_SESSION['pengguna'] = 'Dewi';

var_dump(session_destroy());
var_dump(isset($_SESSION['pengguna']));

$_SESSION = [];
var_dump(isset($_SESSION['pengguna']));
text Keluaran
bool(true)
bool(true)
bool(false)

Keluaran menunjukkan perilaku yang sering mengejutkan. Setelah session_destroy, nilai $_SESSION['pengguna'] masih terbaca karena fungsi itu menghapus data di penyimpanan server dan bukan isi variabel di memori, sehingga kode setelahnya masih menganggap pengguna login. Pengosongan dengan $_SESSION = [] menyelesaikannya, dan cookie pengenal yang tersisa di browser tidak lagi merujuk data apa pun karena ID-nya sudah tidak valid di server.

Kesalahan umum#

Empat kesalahan berikut paling sering muncul saat mengelola session.

  • Session ID tidak diregenerasi sesudah login. ID yang ditanam penyerang tetap sah sehingga session fixation berhasil. Perbaikannya adalah session_regenerate_id(true) segera setelah status login berubah.
  • Proses keluar hanya memanggil session_destroy. Isi $_SESSION masih terbaca dalam skrip yang sama sehingga kode setelahnya menganggap pengguna masih login. Perbaikannya adalah mengosongkan $_SESSION terlebih dahulu, lalu memanggil session_destroy.
  • Cookie session dibiarkan tanpa httponly. Skrip sisipan dari celah XSS dapat membaca cookie dan mengirimkannya ke server penyerang. Perbaikannya adalah httponly bernilai true pada session_set_cookie_params.
  • Masa berlaku session dibiarkan tanpa batas. ID yang bocor tetap hidup berhari-hari sehingga jendela penyalahgunaan memanjang. Perbaikannya adalah mencatat waktu aktivitas di $_SESSION dan menghapus sesi yang melewati batas.

Ringkasan#

  • Session ID berperan sebagai kredensial, sehingga pencurian maupun penetapan ID sama berbahayanya dengan kebocoran kata sandi.
  • session_regenerate_id(true) mengganti ID dan menghapus penyimpanan sesi lama, dan wajib dijalankan sesudah login serta perubahan hak akses.
  • session_set_cookie_params menyetel httponly, secure, dan samesite sebelum session_start dipanggil.
  • Proses keluar yang benar mengosongkan $_SESSION lalu memanggil session_destroy, karena pemanggilan tunggal tidak mengosongkan variabel di memori.

Referensi#