Perilaku session yang kita pakai sejauh ini — lokasi penyimpanan, nama cookie, umur data, hingga perlindungan ID — semuanya dikendalikan oleh direktif konfigurasi di php.ini. Sebagian besar default bawaan PHP dipilih demi kompatibilitas, bukan demi keamanan, sehingga memahami daftar direktif ini adalah syarat sebelum membawa aplikasi ke produksi. Kabar baiknya, seluruh nilai dapat kita baca dari dalam skrip dengan ini_get() dan sebagian besar dapat diubah per-request dengan ini_set(). Chapter ini membedah direktif penting satu per satu, membuktikan efeknya dengan contoh yang dapat dijalankan, lalu membahas custom save handler untuk aplikasi berskala besar.

Membaca Konfigurasi Session dengan ini_get() #

Fungsi ini_get() mengembalikan nilai mentah sebuah direktif sebagai string — string kosong berarti direktif tidak disetel. Skrip berikut mencetak delapan direktif session yang paling sering kita sesuaikan:

php
<?php
$directives = [
    'session.save_path',
    'session.name',
    'session.cookie_lifetime',
    'session.gc_maxlifetime',
    'session.use_strict_mode',
    'session.cookie_httponly',
    'session.cookie_secure',
    'session.cookie_samesite',
];

foreach ($directives as $key) {
    echo str_pad($key, 26) . ' = ' . var_export(ini_get($key), true) . "\n";
}

Output yang Dihasilkan:

text
session.save_path          = ''
session.name               = 'PHPSESSID'
session.cookie_lifetime    = '0'
session.gc_maxlifetime     = '1440'
session.use_strict_mode    = '0'
session.cookie_httponly    = '0'
session.cookie_secure      = '0'
session.cookie_samesite    = ''

Kolom kiri sengaja dirapikan dengan str_pad() agar output mudah dibandingkan baris demi baris. Perhatikan bahwa ini_get() selalu mengembalikan string, termasuk angka seperti '0' dan '1440', sehingga perbandingan numerik perlu konversi eksplisit bila dilakukan di luar var_export().

Direktif Penting dan Anjuran Produksi #

Tabel berikut memetakan direktif yang baru saja kita baca beserta arti, nilai bawaan pada build ini, dan anjuran untuk produksi:

DirektifArtiDefault build iniAnjuran produksi
session.save_pathlokasi penyimpanan file session'' (direktori sementara)direktori khusus di luar /tmp bersama
session.namenama cookie sessionPHPSESSIDnama khas aplikasi (opsional)
session.cookie_lifetimeumur cookie session dalam detik0 (sampai browser ditutup)0; jangan dijadikan andalan keamanan
session.gc_maxlifetimeumur data session sebelum jadi sampah1440sesuaikan, mis. 1800
session.use_strict_modemenolak ID yang tidak dikenal01
session.cookie_httponlymelarang JavaScript membaca cookie session01
session.cookie_securehanya kirim cookie melalui HTTPS01
session.cookie_samesiteaturan pengiriman lintas situs'' (browser menentukan)Lax atau Strict

Mengubah Konfigurasi pada Runtime #

ini_set() mengubah nilai direktif hanya untuk request yang sedang berjalan — nilainya kembali ke php.ini pada request berikutnya. Untuk session, perubahan harus dilakukan sebelum session_start() dan sebelum output apa pun, karena PHP menolak mengubah pengaturan session setelah header terkirim.

php
<?php
$sebelumLifetime = ini_get('session.gc_maxlifetime');
$sebelumName = ini_get('session.name');

ini_set('session.gc_maxlifetime', '3600');
ini_set('session.name', 'SESSID_TOKO');

$sesudahLifetime = ini_get('session.gc_maxlifetime');
$sesudahName = ini_get('session.name');

echo "gc_maxlifetime sebelum: $sebelumLifetime\n";
echo "gc_maxlifetime sesudah: $sesudahLifetime\n";
echo "name sesudah: $sesudahName\n";

Output yang Dihasilkan:

text
gc_maxlifetime sebelum: 1440
gc_maxlifetime sesudah: 3600
name sesudah: SESSID_TOKO

Skrip menangkap nilai lama terlebih dahulu, mengubah dua direktif, lalu mencetak nilai sebelum dan sesudah — pola tangkap-dulu-cetak-belakangan yang sama seperti pada chapter Sessions, karena ini_set() untuk session tidak boleh didahului output. Pola ini lazim dipasang di front controller: satu titik di awal aplikasi yang menyetel seluruh pengaturan session sebelum route mana pun dieksekusi. Untuk perubahan permanen, gunakan php.ini atau konfigurasi web server, bukan ini_set().

Menyelidiki session.save_path dan Handler files #

Direktif session.save_path menentukan di mana handler files — handler bawaan PHP — menaruh file sess_<id>. Pada build CLI ini nilainya kosong, dan PHP diam-diam jatuh ke direktori sementara sistem. Kita bisa membuktikannya:

php
<?php
$savePath = ini_get('session.save_path');
$handler = session_module_name();
$tmpDir = sys_get_temp_dir();

session_id('cek-default-path');
session_start();
$_SESSION['tanda'] = 'ada';
session_write_close();
$fileAda = file_exists($tmpDir . '/sess_cek-default-path');
@unlink($tmpDir . '/sess_cek-default-path');

echo 'save_path: ' . var_export($savePath, true) . "\n";
echo 'handler aktif: ' . $handler . "\n";
echo 'File tersimpan di direktori sementara: ' . var_export($fileAda, true) . "\n";

Output yang Dihasilkan:

text
save_path: ''
handler aktif: files
File tersimpan di direktori sementara: true

Nilai save_path yang kosong ternyata tetap menghasilkan file session di direktori sementara sistem — session_module_name() menegaskan bahwa handler aktif bernama files. Pada hosting bersama, direktori sementara seperti /tmp dapat dibaca proses pengguna lain di server yang sama, sehingga file session kita berisiko terintip. Solusinya adalah menyetel session.save_path ke direktori khusus aplikasi dengan izin yang ketat, atau beralih ke handler penyimpanan lain.

Strict Mode untuk Menolak ID Palsu #

Direktif session.use_strict_mode menentukan sikap PHP terhadap ID yang datang dari cookie namun tidak dikenal. Mode ketat menolak ID yang belum pernah dibuat PHP dan menggantinya dengan ID acak baru; mode longgar menerima ID apa pun dan membuat penyimpanannya. Perbedaan keduanya terlihat jelas bila kita mensimulasikan cookie session buatan penyerang:

php
<?php
$dir = sys_get_temp_dir() . '/php-kursus-sesi';
if (!is_dir($dir)) {
    mkdir($dir);
}
session_save_path($dir);
@unlink($dir . '/sess_id-pilihan-penyerang');

// Simulasi browser mengirim cookie PHPSESSID buatan penyerang
$_COOKIE['PHPSESSID'] = 'id-pilihan-penyerang';
session_start();
$idTanpaStrict = session_id();
session_write_close();

// Bandingkan dengan strict mode aktif
ini_set('session.use_strict_mode', '1');
$_COOKIE['PHPSESSID'] = 'id-pilihan-penyerang-2';
session_start();
$idDenganStrict = session_id();

@unlink($dir . '/sess_' . $idDenganStrict);
@unlink($dir . '/sess_id-pilihan-penyerang');

echo 'Strict OFF, ID cookie dipakai apa adanya: '
    . var_export($idTanpaStrict === 'id-pilihan-penyerang', true) . "\n";
echo 'Strict ON, ID cookie ditolak dan diganti: '
    . var_export($idDenganStrict !== 'id-pilihan-penyerang-2', true) . "\n";

Output yang Dihasilkan:

text
Strict OFF, ID cookie dipakai apa adanya: true
Strict ON, ID cookie ditolak dan diganti: true

Tanpa strict mode, PHP menuruti ID apa pun yang dikirim cookie — pintu masuk klasik serangan session fixation, karena penyerang bisa memilih ID yang sudah ia ketahui lalu menunggu korban login dengan ID tersebut. Dengan strict mode, ID pilihan penyerang ditolak dan diganti ID acak yang dibuat PHP sendiri. Strict mode adalah lapisan pertama; lapisan kedua adalah mengganti ID setelah login, yang menjadi topik chapter Session Regeneration .

Custom Save Handler untuk Skala Besar #

Handler files cukup untuk satu server, tetapi aplikasi modern biasanya berjalan di beberapa server di belakang load balancer. Request pengguna yang jatuh ke server berbeda akan kehilangan session bila datanya tersebar di disk masing-masing. Solusinya adalah memindahkan penyimpanan ke layanan bersama — Redis, Memcached, atau database — melalui session_set_save_handler():

php
<?php
class RedisSessionHandler implements SessionHandlerInterface
{
    public function open(string $path, string $name): bool
    {
        // buka koneksi ke server Redis
        return true;
    }

    public function close(): bool
    {
        // tutup koneksi
        return true;
    }

    public function read(string $id): string|false
    {
        // GET sess:<id> dari Redis
        return '';
    }

    public function write(string $id, string $data): bool
    {
        // SETEX sess:<id> <gc_maxlifetime> <data>
        return true;
    }

    public function destroy(string $id): bool
    {
        // DEL sess:<id>
        return true;
    }

    public function gc(int $max_lifetime): int|false
    {
        // hapus kunci yang kedaluwarsa; Redis dapat memakai TTL
        return 0;
    }
}

// $handler = new RedisSessionHandler();
// session_set_save_handler($handler, true);
// session_start();

Kelas di atas mengimplementasikan SessionHandlerInterface — enam metode yang menggantikan baca-tulis-hapus file dengan operasi pada penyimpanan pilihan kita. PHP tetap menjalankan alur $_SESSION seperti biasa; yang berubah hanya ke mana data pergi dan dari mana data dimuat. Redis memang pilihan populer karena kedaluwarsa per kunci (TTL) membuat pekerjaan gc() hampir tidak diperlukan. Ekstensi redis tersedia di lingkungan ini, namun tanpa server Redis yang berjalan, contoh di atas sengaja berupa kerangka implementasi untuk dipelajari strukturnya.

Ringkasan #

Konfigurasi session hidup di belasan direktif php.ini yang dapat dibaca dengan ini_get() dan diubah per-request dengan ini_set() sebelum session_start(). Default build ini membiarkan strict mode, httponly, dan secure dalam keadaan mati, sehingga aplikasi produksi wajib menyetelnya secara eksplisit. save_path yang kosong menjatuhkan file session ke direktori sementara, dan bila aplikasi tumbuh melintasi banyak server, session_set_save_handler() mengalihkan penyimpanan ke Redis, Memcached, atau database. Dengan konfigurasi yang benar, satu ancaman terakhir tetap menuntut perhatian aktif di kode: mengganti ID session pada momen yang tepat — topik chapter penutup rangkaian ini.

Referensi #