CSRF (Cross-Site Request Forgery) — Security

Apa itu CSRF? CSRF (Cross-Site Request Forgery) adalah serangan yang memaksa browser pengguna yang sudah login mengirimkan request ke website target tanpa…

CSRF (Cross-Site Request Forgery, "pemalsuan request lintas situs") adalah serangan yang bikin browser korban mengirim request ke situs tempat dia sudah login, tanpa dia sadari. Kuncinya: browser otomatis menempelkan cookie ke setiap request ke situs itu, siapa pun yang menyuruhnya. Bayangin kurir langganan yang selalu menempelkan kartu member-mu di setiap paket ke toko langganan, gak peduli siapa yang menitipkan paketnya. Orang asing cukup menitip paket berisi "transfer sepuluh juta", dan toko melihat kartu member-mu di situ.

🧪 Tebakan cepat: kamu login di bank.example, dan cookie session-mu pakai SameSite=Lax. Lalu kamu membuka evil.example, yang diam-diam mengirim form POST ke bank.example/transfer. Cookie-mu ikut terkirim atau gak? Tebak dulu, jawabannya ada di bagian "SameSite: kapan cookie ikut terkirim".

Gimana serangannya terjadi

Di Lab, Budi login di bank.example dengan saldo Rp15.000.000. Server memberi cookie session=7f3a9c. Selama cookie itu ada, setiap request ke bank.example dianggap datang dari Budi.

Lalu Budi membuka evil.example dari link di email. Halaman itu menyuruh browser Budi mengirim form POST ke https://bank.example/transfer, tanpa Budi menekan apa pun. Form boleh dikirim ke situs mana pun, jadi browser menurut.

Yang tiba di server kalau cookie-nya boleh ikut:

POST /transfer HTTP/1.1
Host: bank.example
Origin: https://evil.example
Cookie: session=7f3a9c

to=hacker-account&amount=10000000

Server melihat session=7f3a9c, mengenali Budi, dan memindahkan Rp10.000.000. Saldo tinggal Rp5.000.000. Server gak bisa membedakan request ini dari klik Budi sendiri, karena dia cuma melihat cookie. Penyerang juga gak pernah melihat cookie-nya: browser yang menempelkannya.

CSRF butuh tiga syarat: korban sedang login dengan cookie, aksinya cukup satu request yang isinya bisa ditebak, dan server cuma mengandalkan cookie untuk tahu siapa pengirimnya.

SameSite: kapan cookie ikut terkirim

Atribut SameSite di cookie mengatur apakah cookie ikut di request yang datang dari situs lain (cross-site). Server memasangnya waktu mengirim cookie:

Set-Cookie: session=7f3a9c; Path=/; Secure; HttpOnly; SameSite=Lax
Request ke bank.example None Lax Strict
Form POST dari evil.example ikut gak ikut gak ikut
Klik link dari evil.example (GET, pindah halaman) ikut ikut gak ikut
<img> atau fetch dari evil.example ikut gak ikut gak ikut
Request dari bank.example sendiri ikut ikut ikut

Jadi dengan Lax, form POST dari evil.example dikirim tanpa cookie. Server gak mengenali Budi, transfer gak terjadi. Itu jawaban tebakannya.

CSRF token: rahasia yang gak bisa ditebak penyerang

SameSite saja belum cukup, karena aturannya beda di tiap browser. Pertahanan utama adalah CSRF token: string acak yang dibuat server untuk tiap session, ditaruh di setiap form, lalu dicocokkan saat form dikirim. Halaman evil.example gak bisa membaca isi halaman bank.example (aturan same-origin browser), jadi dia gak tahu token-nya.

Di Laravel, cukup tulis @csrf di form Blade:

<form method="POST" action="/transfer">
  @csrf
  <input name="to">
  <input name="amount">
  <button>Transfer</button>
</form>

Form palsu di evil.example gak punya _token yang benar, jadi ditolak 419, walau cookie-nya ikut.

Memeriksa token di server

Ini inti pengecekannya, ditulis sebagai function biasa:

function validateCSRFToken(sessionToken, requestToken) {
  if (!sessionToken || !requestToken) return false; // kosong, null, undefined: tolak
  return sessionToken === requestToken; // harus sama persis
}

console.log(validateCSRFToken('abc123', 'abc123')); // true
console.log(validateCSRFToken('abc123', 'xyz789')); // false
console.log(validateCSRFToken('abc123', '')); // false
Panggilan Hasil Kenapa
validateCSRFToken('abc123', 'abc123') true sama persis
validateCSRFToken('abc123', 'xyz789') false beda
validateCSRFToken('abc123', '') false token request kosong
validateCSRFToken('abc123', null) false token request gak ada
validateCSRFToken('', '') false dua-duanya kosong

Laravel sendiri membandingkan dengan hash_equals, yang butuh waktu sama panjang entah token-nya cocok di huruf pertama atau terakhir, jadi waktunya gak membocorkan apa pun.

💡 Origin header dan API dengan Bearer token

Browser juga mengirim header Origin di request POST. Server bisa menolak request yang Origin-nya bukan https://bank.example, sebagai lapisan tambahan di samping token.

API yang login-nya pakai header Authorization: Bearer ... (bukan cookie) secara alami aman dari CSRF: browser gak pernah menempelkan header itu sendiri, jadi evil.example gak bisa memakainya. Begitu API-mu login pakai cookie, perlakukan sama seperti form: pasang token.

⚠️ Jebakan yang sering ketemu

TL;DR: CSRF (Cross-Site Request Forgery) memanfaatkan browser yang otomatis menempelkan cookie: halaman evil.example menyuruh browser korban mengirim POST /transfer ke bank.example, dan server mengira itu korban. SameSite=Lax menahan cookie di form POST dan <img> dari situs lain, tapi tetap mengirimnya saat link diklik; Strict menahan semuanya; None selalu mengirim. Pertahanan utama adalah CSRF token: di Laravel @csrf menaruh _token di form, dan token yang gak cocok ditolak dengan 419. Pengecekannya: tolak token kosong, null atau undefined dulu, lalu bandingkan dengan ===. Jangan pernah mengubah data lewat GET, dan form POST tanpa CSRF token itu rentan.

Yang akan kamu pelajari