keamanansessionloginMahir4 mnt baca

Mengamankan Session

Tutup celah session: fixation, hijacking, timeout, dan remember-me yang aman.

Kenapa Session Perlu Diamankan Khusus?

Session ID adalah kunci kerajaan: siapa pun yang memegangnya dianggap sudah login, tanpa perlu password. Tiga serangan mengincarnya: fixation (penyerang menanam ID ke korban sebelum login), hijacking (penyerang mencuri ID korban, misal via XSS atau sniffing), dan session abadi (ID tidak pernah kedaluwarsa, memperbesar jendela serangan).

php
<?php
// Contoh 1: pertahanan berlapis di auth.php
session_start();

// 1. Regenerate ID setiap login (anti fixation) dan berkala
if (!isset($_SESSION["regenerasi"])) {
    $_SESSION["regenerasi"] = time();
} elseif (time() - $_SESSION["regenerasi"] > 1800) {
    session_regenerate_id(true);   // ID baru tiap 30 menit
    $_SESSION["regenerasi"] = time();
}

// 2. Idle timeout: tendang setelah 30 menit tidak aktif
$timeout = 1800;
if (isset($_SESSION["terakhir_aktif"]) && time() - $_SESSION["terakhir_aktif"] > $timeout) {
    session_destroy();
    header("Location: login.php?alasan=timeout");
    exit;
}
$_SESSION["terakhir_aktif"] = time();

// 3. Ikat session ke user agent (deteksi pencurian kasar)
$ua = $_SERVER["HTTP_USER_AGENT"] ?? "";
if (!isset($_SESSION["ua"])) {
    $_SESSION["ua"] = $ua;
} elseif ($_SESSION["ua"] !== $ua) {
    session_destroy();   // user agent berubah = kemungkinan hijack
    header("Location: login.php?alasan=keamanan");
    exit;
}

Regenerasi berkala mempersempit jendela ID curian: ID yang dicuri kemarin sudah tidak berlaku. Idle timeout menutup sesi yang ditinggal di komputer umum. Ikatan user-agent bukan sempurna (bisa dipalsukan), tapi menggagalkan serangan oportunis.

php
<?php
// Contoh 2: remember-me aman dengan token (bukan perpanjang session!)
// Saat login + centang "ingat saya":
function buatRememberToken(int $userId): string {
    $token = bin2hex(random_bytes(32));          // token mentah ke cookie
    $hash = hash("sha256", $token);             // hash ke database!
    // simpan: INSERT INTO remember_tokens (user_id, token_hash, kedaluwarsa)
    setcookie("ingat", $token, time() + 30 * 86400, "/", "", false, true);
    return $token;
}

// Saat session habis tapi cookie "ingat" ada:
function cekRemember(PDO $pdo): void {
    if (isset($_SESSION["user_id"]) || empty($_COOKIE["ingat"])) return;
    $hash = hash("sha256", $_COOKIE["ingat"]);
    // SELECT user_id FROM remember_tokens WHERE token_hash = ? AND kedaluwarsa > NOW()
    // jika cocok: login otomatis + regenerate + BUAT TOKEN BARU (rotasi)
}

Prinsipnya: cookie menyimpan token mentah, database menyimpan hash-nya (seperti password). Token dirotasi setiap dipakai. Kalau database bocor, penyerang hanya dapat hash yang tidak bisa dipakai sebagai cookie.

Jebakan yang Sering Terjadi

session.use_only_cookies mati. Secara default modern PHP sudah 1, tapi pastikan: ID session tidak boleh dikirim via URL (?PHPSESSID=...) karena URL bocor ke history, log, dan referer. Cek dengan ini_get("session.use_only_cookies").

Cookie session tanpa flag aman. Set di php.ini atau kode: session.cookie_httponly=1 (anti curi via XSS), session.cookie_samesite=Lax (anti CSRF), dan session.cookie_secure=1 di production HTTPS (hanya dikirim via HTTPS).

Deteksi Anomali Sederhana

Selain tiga pertahanan utama, tambahkan deteksi anomali ringan:

php
<?php
session_start();
// Hitung request per menit per session (anti otomasi)
$menit = (int) date("i");
if (($_SESSION["menit"] ?? -1) !== $menit) {
    $_SESSION["menit"] = $menit;
    $_SESSION["hitung"] = 1;
} else {
    $_SESSION["hitung"]++;
    if ($_SESSION["hitung"] > 120) {
        // lebih dari 120 request/menit: kemungkinan bot
        sleep(2);   // perlambat, jangan langsung blokir (hindari false positive)
    }
}

sleep() sebagai respons lebih elegan daripada blokir langsung: bot melambat drastis, manusia normal tidak merasakan apa-apa (mereka tidak pernah 120 request/menit). Untuk proteksi serius, pindahkan hitungan ke Redis/database agar bertahan antar session.

Catatan teknis: Untuk "logout dari semua perangkat", simpan session_version di database per user dan di session. Saat user ganti password, naikkan versinya: semua session dengan versi lama otomatis tidak valid di guard. Satu kolom, fitur keamanan premium.

Tantangan

Guard dengan idle timeout

Tulis file auth.php yang: memanggil session_start(), menendang ke login.php jika $_SESSION["user_id"] tidak ada ATAU idle lebih dari 1800 detik ($_SESSION["terakhir_aktif"]), lalu memperbarui terakhir_aktif setiap request.