Apa Itu CSRF

pindipin
19 October 2026
14 min read

Bayangkan skenario Kamu sedang login ke aplikasi internet banking milik kamu di domain bank-anda.com. Di tab terpisah pada browser yang sama, kamu membuka sebuah situs forum atau blog hiburan yang ternyata dikelola oleh peretas (situs-jahat.com). Tanpa kamu sadari, begitu halaman situs-jahat.com terbuka, uang senilai puluhan juta rupiah dari rekening kamu tiba-tiba berpindah ke rekening peretas. Kamu tidak pernah mengklik tombol transfer, tidak pernah memasukkan PIN ulang, dan tidak melihat adanya keanehan pada layar tab bank kamu.

Bagaimana hal ini bisa terjadi? Jawabannya terletak pada salah satu kerentanan web paling klasik namun tetap mematikan hingga saat ini: Cross-Site Request Forgery (CSRF).

Dalam artikel ini, kita akan membongkar secara mendalam apa itu CSRF, mengapa browser mengizinkan serangan ini terjadi, perbedaan kritis CSRF dengan CORS dan XSS, serta panduan arsitektur pencegahan praktis untuk developer PHP (Laravel) dan JavaScript (Node.js/Express).


1. Pendahuluan & Pengertian CSRF

Apa Itu CSRF?

Cross-Site Request Forgery (CSRF atau XSRF) adalah jenis serangan keamanan web yang mengeksploitasi kepercayaan (trust) yang dimiliki oleh aplikasi web terhadap browser pengguna yang sedang terautentikasi.

Dalam serangan CSRF, penyerang tidak perlu mencuri kata sandi atau kredensial pengguna secara langsung. Sebaliknya, penyerang "menjebak" browser korban untuk mengirimkan HTTP Request yang melakukan aksi berbahaya (seperti mengubah alamat email, mengganti password, atau melakukan transaksi keuangan) ke aplikasi target di mana korban saat ini memiliki sesi aktif (logged in).

Karena alasan ini, CSRF sering disebut juga sebagai Session Riding atau One-Click Attack.

Akar masalah utama dari kerentanan CSRF terletak pada perilaku default (default behavior) dari browser web HTTP.

Secara standar arsitektur web:

  1. Ketika pengguna berhasil login ke bank-anda.com, server akan merespons dengan header Set-Cookie: session_id=abc123xyz.
  2. Untuk setiap HTTP Request berikutnya yang ditujukan ke bank-anda.com, browser secara otomatis menyertakan cookie session_id tersebut di dalam header request (Cookie: session_id=abc123xyz).
  3. Browser melakukan hal ini secara implisit tanpa peduli situs mana yang menginisiasi request tersebut.

Jika kamu berada di situs-jahat.com dan situs tersebut memicu request ke bank-anda.com/api/transfer, browser akan tetap menempelkan cookie session milik bank-anda.com. Dari sudut pandang server bank-anda.com, request yang masuk membawa cookie session yang valid, sehingga server menganggap request tersebut sah berasal dari keinginan pengguna.


2. Mekanisme Serangan CSRF (Langkah demi Langkah)

Untuk memahami bagaimana CSRF berjalan di dunia nyata, mari kita bedah alur eksploitasinya step-by-step.

Skenario Eksploitasi 1: Form Submit Otomatis (POST Request)

Misalkan aplikasi bank-anda.com memiliki fitur transfer uang yang diproses melalui rute POST https://bank-anda.com/api/transfer dengan bodi parameter:

  • destination_account
  • amount

Peretas membuat situs situs-jahat.com yang berisi kode HTML dan JavaScript berikut:

<!-- Halaman di situs-jahat.com -->
<!DOCTYPE html>
<html>
<head>
    <title>Selamat Kamu Mendapatkan Hadiah!</title>
</head>
<body>
    <h1>Tunggu sejenak, sedang memproses hadiah...</h1>

    <!-- Form tersembunyi yang mengarah ke server bank -->
    <form id="csrfForm" action="https://bank-anda.com/api/transfer" method="POST">
        <input type="hidden" name="destination_account" value="999000-REK-PERETAS" />
        <input type="hidden" name="amount" value="10000000" />
    </form>

    <script>
        // Otomatis submit form begitu halaman selesai dimuat
        document.addEventListener("DOMContentLoaded", function() {
            document.getElementById('csrfForm').submit();
        });
    </script>
</body>
</html>

Ketika korban yang sedang memiliki sesi aktif di bank-anda.com membuka halaman tersebut:

  1. Browser mengunduh dokumen HTML dari situs-jahat.com.
  2. JavaScript mengeksekusi submit() pada form.
  3. Browser mengirimkan permintaan POST https://bank-anda.com/api/transfer.
  4. Browser secara otomatis menyertakan header Cookie: session_id=xyz... milik bank-anda.com.
  5. Server bank-anda.com menerima request, membaca cookie session, memverifikasi bahwa korban memang sudah login, dan mengeksekusi pemindahan dana sebesar Rp 10.000.000 ke akun peretas.

Skenario Eksploitasi 2: Tag Gambar / Resource Embedding (GET Request ber-Side-Effect)

Jika aplikasi web yang buruk mengizinkan perubahan data (state-changing) melalui HTTP GET Request (misalnya: https://bank-anda.com/email/update?email=baru@gmail.com), peretas bahkan tidak memerlukan JavaScript atau form!

Peretas cukup menyisipkan tag gambar di postingan forum atau email HTML:

<img src="https://bank-anda.com/email/update?email=peretas@hacker.com" width="0" height="0" />

Saat browser korban mencoba memuat gambar dari URL tersebut, browser mengirimkan request GET beserta cookie session. Server bank langsung memproses perubahan email akun korban tanpa ada konfirmasi tambahan.


3. Perbedaan Kritis: CSRF vs XSS vs CORS

Banyak developer web pemula sering kali tertukar antara CSRF, XSS, dan CORS policy. Menggabungkan ketiga konsep ini tanpa pemahaman yang benar dapat menimbulkan celah keamanan yang serius.

Perbandingan Ringkas

Fitur / ParameterCSRF (Cross-Site Request Forgery)XSS (Cross-Site Scripting)CORS (Cross-Origin Resource Sharing)
Fokus KerentananMemaksa browser mengeksekusi tindakan/request ke domain lain.Menyisipkan dan mengeksekusi skrip JavaScript liar di dalam DOM aplikasi target.Mekanisme keamanan browser untuk membatasi akses pembacaan response cross-origin.
Arah SeranganDari situs luar (Cross-Origin) menembus server target.Di dalam situs aplikasi target itu sendiri (Same-Origin).Bukan serangan, melainkan fitur proteksi browser.
Apakah Attacker Bisa Membaca Respon?TIDAK. Peretas hanya memicu eksekusi, tetapi tidak bisa melihat isi balasan server.YA. Peretas bisa membaca isi DOM, cookie (non-HttpOnly), local storage, dan API response.Browser memblokir pembacaan respon jika CORS tidak mengizinkan.
Penyebab UtamaBrowser otomatis menyertakan cookie pada request cross-origin.Kurangnya sanitasi & escaping pada input/output HTML.Konfigurasi header HTTP Access-Control-Allow-Origin di server.

Kesalahpahaman Umum: "CORS Sudah Cukup Mencegah CSRF"

Ini adalah mitos keamanan paling populer dan paling berbahaya di kalangan developer.

Fakta: CORS TIDAK dirancang untuk mencegah serangan CSRF.

Mengapa demikian?
CORS (Cross-Origin Resource Sharing) bertugas mengontrol apakah skrip JavaScript dari situs A diizinkan untuk membaca respon yang dikembalikan oleh situs B.

Namun pada serangan CSRF:

  1. Standard HTML Form Submission (<form action="..." method="POST">) TIDAK memicu pemeriksaan CORS preflight (simple request).
  2. Permintaan POST dari form tetap akan terkirim dan dieksekusi oleh server target.
  3. Walaupun browser kemudian menyembunyikan respon dari peretas karena kebijakan CORS, kerusakan pada database server target sudah terlanjur terjadi (transaksi sudah diproses).

Oleh karena itu, memasang konfigurasi CORS yang ketat sekalipun tidak akan melindungi aplikasi kamu dari eksploitasi CSRF berbasis form HTML.


4. Strategi & Mekanisme Defensif Utama

Untuk mengamankan aplikasi dari CSRF, industri keamanan web mengembangkan beberapa standar arsitektur pertahanan.

4.1 Synchronizer Token Pattern (Anti-CSRF Token)

Ini adalah teknik perlindungan CSRF paling standar dan teruji, yang digunakan oleh mayoritas framework web monolithic (Laravel, Django, Ruby on Rails).

Cara Kerja:

  1. Saat pengguna membuat sesi di server, server membuat sebuah string acak yang aman secara kriptografi (cryptographically secure random token), misalnya CSRF-TOKEN-a1b2c3d4....
  2. Token ini disimpan di sesi server side (atau disinkronkan dengan sesi pengguna).
  3. Setiap kali server merender form HTML yang memiliki metode mutasi (POST, PUT, PATCH, DELETE), server menyisipkan token ini ke dalam field tersembunyi (hidden input field).
  4. Ketika form dikirimkan, client wajib mengirimkan nilai token tersebut kembali ke server.
  5. Middleware di server akan membandingkan token dari request dengan token yang ada di sesi server. Jika tidak cocok atau tidak ada, request langsung ditolak (HTTP 419 Page Expired atau HTTP 403 Forbidden).
<!-- Contoh Form dengan Synchronizer Token Pattern -->
<form method="POST" action="/profile/update">
    <!-- Hidden input berisi CSRF Token -->
    <input type="hidden" name="_token" value="d9a8f7e6c5b4a321...">

    <label>Email Baru:</label>
    <input type="email" name="email">
    <button type="submit">Simpan</button>
</form>

Mengapa Peretas Tidak Bisa Mencuri Token Ini?

Berdasarkan aturan Same-Origin Policy (SOP) browser, JavaScript dari situs-jahat.com TIDAK dapat membaca isi DOM dari halaman bank-anda.com. Karena peretas tidak bisa membaca isi dokumen HTML korban di bank-anda.com, peretas tidak bisa mengambil nilai _token tersembunyi untuk dimasukkan ke dalam payload serangan mereka.

Teknik ini umumnya digunakan pada arsitektur aplikasi stateless atau Single Page Application (SPA) yang tidak menyimpan data sesi di memori server-side.

Cara Kerja:

  1. Server membuat token CSRF acak dan mengirimkannya ke client dalam bentuk Cookie (misal nama cookienya XSRF-TOKEN). Cookie ini dapat dibaca oleh JavaScript client-side (HttpOnly: false).
  2. Ketika aplikasi client (React, Vue, Angular, Axios) hendak melakukan request AJAX (POST, PUT, DELETE), JavaScript membaca nilai dari cookie XSRF-TOKEN tersebut.
  3. JavaScript kemudian menduplikasi nilai token tersebut ke dalam Custom HTTP Request Header (misal: X-XSRF-TOKEN atau X-CSRF-TOKEN).
  4. Server memverifikasi apakah isi dari Custom HTTP Header sama dengan isi dari Cookie yang ikut terkirim.

Mengapa Teknik Ini Aman?

Meskipun browser peretas secara otomatis menyertakan cookie XSRF-TOKEN saat mengirimkan request cross-site, skrip peretas di situs-jahat.com tidak bisa membaca isi cookie domain target untuk mengekstrak string token dan memasukkannya ke dalam header X-XSRF-TOKEN. Karena header kustom tersebut kosong atau salah, server akan menolak request tersebut.

Atribut SameSite pada cookie adalah mekanisme pertahanan modern dari sisi browser yang memberikan kontrol penuh kepada developer atas kapan cookie boleh dikirim pada request cross-site.

Header Set-Cookie mendukung tiga nilai SameSite:

1. SameSite=Strict

  • Cookie TIDAK PERNAH dikirim dalam permintaan cross-site apa pun.
  • Bahkan jika pengguna mengklik link biasa dari email atau WhatsApp yang mengarah ke bank-anda.com, cookie sesi tidak akan dikirim pada request pertama tersebut (pengguna akan terlihat seperti belum login sampai halaman direfresh/donavigasikan dari internal domain).
  • Penggunaan: Aplikasi dengan tingkat keamanan ultra tinggi (sistem perbankan, portal militer).

2. SameSite=Lax (Default pada Browser Modern)

  • Cookie TIDAK dikirim pada permintaan cross-site yang berpotensi mengubah state (POST, PUT, DELETE, AJAX cross-site).
  • Cookie TETAP dikirim pada navigasi tingkat atas (top-level navigation) dengan HTTP method yang aman/idempotent (seperti mengklik link biasa <a href="..."> dengan method GET).
  • Penggunaan: Standar ideal untuk mayoritas aplikasi web modern (menyeimbangkan keamanan dan kenyamanan pengguna).

3. SameSite=None

  • Cookie dikirim pada semua request cross-site (seperti perilaku cookie tradisional).
  • Syarat Wajib: Wajib mengaktifkan atribut Secure (Set-Cookie: session=123; SameSite=None; Secure). Ini berarti cookie hanya dikirim melalui koneksi terenkripsi HTTPS.

4.4 Autentikasi Berbasis Token (JWT dalam Header Authorization)

Jika aplikasi SPA/Mobile kamu menggunakan mekanisme autentikasi stateless dengan token JWT yang disimpan dalam memori (variabel JS) dan dikirim via header:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Secara alami, aplikasi kamu kebal terhadap CSRF. Mengapa? Karena browser tidak pernah menyertakan header Authorization: Bearer ... secara otomatis pada request cross-site atau form submit. Header ini harus disuntikkan secara eksplisit via JavaScript.

Peringatan: Jika JWT kamu disimpan di dalam HTTP Cookie, maka risiko CSRF tetap ada, dan kamu wajib menerapkan atribut SameSite atau CSRF Token!


5. Panduan Implementasi Praktis

5.1 Proteksi CSRF di PHP / Laravel

Framework Laravel menyediakan perlindungan CSRF yang sangat matang secara out-of-the-box melalui middleware VerifyCsrfToken (pada Laravel 11+, dikelola via bootstrap/app.php).

A. Menggunakan Blade Directive

Saat kamu membuat form HTML di Blade, kamu cukup menambahkan directive @csrf:

<!-- resources/views/profile.blade.php -->
<form action="/user/update" method="POST">
    @csrf

    <input type="text" name="username" value="{{ auth()->user()->name }}">
    <button type="submit">Update Profil</button>
</form>

Directive @csrf ini akan otomatis dirender menjadi:

<input type="hidden" name="_token" value="Qe1r2t3y4u5i6o7p8a9s0d...">

B. Setup HTTP AJAX (Axios / Fetch)

Laravel secara otomatis membuat cookie XSRF-TOKEN terenkripsi pada setiap respon. Library HTTP populer seperti Axios secara otomatis membaca cookie ini dan melampirkannya ke header X-XSRF-TOKEN pada setiap request mutasi data.

Jika kamu menggunakan JavaScript fetch manual, kamu perlu mengambil token dari tag <meta>:

<!-- Di bagian <head> HTML -->
<meta name="csrf-token" content="{{ csrf_token() }}">
// Di file JavaScript kamu
const token = document.querySelector('meta[name="csrf-token"]').getAttribute('content');

fetch('/user/update', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-CSRF-TOKEN': token
    },
    body: JSON.stringify({ username: 'Budi' })
});

C. Dikecualikan dari Proteksi CSRF (Exempting Routes)

Beberapa rute, seperti Webhook dari Payment Gateway (Midtrans, Stripe) atau callback eksternal, tidak dapat menyertakan CSRF token karena dikirim dari server pihak ketiga.

Pada Laravel 11+, kamu dapat mengecualikan rute tersebut melalui bootstrap/app.php:

// bootstrap/app.php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->validateCsrfTokens(except: [
            'stripe/*',
            'api/midtrans/notification',
        ]);
    })->create();

5.2 Proteksi CSRF di JavaScript / Node.js (Express.js)

Catatan Penting: Package legacy csurf untuk Express.js sudah didepresiasi (deprecated) dan sebaiknya tidak digunakan lagi untuk proyek baru.

Untuk aplikasi Node.js modern berbasis Express.js, pendekatan terbaik adalah menggabungkan konfigurasi cookie SameSite yang ketat dengan paket pengelola token aktif seperti csrf-csrf (Double Submit Cookie Pattern).

Langkah pertama dan termudah adalah memastikan sesi Express kamu dilindungi oleh atribut SameSite.

// server.js
const express = require('express');
const session = require('express-session');

const app = express();

app.use(session({
    secret: 'kunci-rahasia-super-aman-12345',
    resave: false,
    saveUninitialized: false,
    cookie: {
        httpOnly: true, // Mencegah akses cookie dari JavaScript (XSS Protection)
        secure: process.env.NODE_ENV === 'production', // Wajib HTTPS di production
        sameSite: 'lax' // Memblokir cookie pada cross-site POST form CSRF
    }
}));

B. Menggunakan Library csrf-csrf untuk SPA / Modern Web

Jika kamu membutuhkan validasi CSRF Token berbasis Double-Submit Cookie di Express:

  1. Install dependency:
npm install csrf-csrf cookie-parser
  1. Konfigurasi middleware di Express:
const express = require('express');
const cookieParser = require('cookie-parser');
const { doubleCsrf } = require('csrf-csrf');

const app = express();

app.use(express.json());
app.use(cookieParser('cookie-secret-key'));

// System options untuk csrf-csrf
const {
    doubleCsrfProtection, // Middleware validasi
    generateToken // Function pembentuk token
} = doubleCsrf({
    getSecret: () => 'SecretKunciSangatRahasia123',
    cookieName: 'x-csrf-token',
    cookieOptions: {
        httpOnly: true,
        sameSite: 'lax',
        secure: process.env.NODE_ENV === 'production',
    },
    size: 64,
    ignoredMethods: ['GET', 'HEAD', 'OPTIONS'],
});

// Endpoint untuk mendaptkan CSRF Token bagi Client
app.get('/api/csrf-token', (req, res) => {
    const token = generateToken(req, res);
    res.json({ csrfToken: token });
});

// Proteksi rute mutasi data dengan middleware doubleCsrfProtection
app.post('/api/transfer', doubleCsrfProtection, (req, res) => {
    // Jika lolos dari middleware, berarti CSRF token valid
    res.json({ status: 'success', message: 'Transfer berhasil diproses' });
});

app.listen(3000, () => console.log('Server berjalan pada port 3000'));

6. Best Practices & Common Pitfalls

Security Checklist & Best Practices

  1. Patuhi Prinsip Semantik HTTP Method:
    • Jangan pernah mengizinkan aksi yang mengubah data (state-changing action) dilakukan melalui metode GET.
    • Gunakan GET hanya untuk operasi pembacaan yang aman (idempotent & safe).
  2. Terapkan Strategi Defense-in-Depth:
    • Jangan hanya mengandalkan satu lapisan. Kombinasikan SameSite Cookie (Lax/Strict) + Anti-CSRF Token + Cookie Flag HttpOnly & Secure.
  3. Validasi Origin & Referer Header:
    • Untuk request mutasi data, buat middleware di server untuk memverifikasi apakah header Origin atau Referer cocok dengan domain utama aplikasi kamu.
  4. Proteksi Aplikasi dari XSS:
    • Proteksi CSRF terbaik sekalipun akan runtuh jika aplikasi kamu memiliki celah Cross-Site Scripting (XSS). Jika peretas bisa menginjeksikan skrip JS di domain kamu (XSS), mereka dapat dengan mudah mengambil CSRF token dan membaca data sensitif.

Pitfalls (Kesalahan Fatal yang Harus Dihindari)

  • Mematikan CSRF Middleware Secara Global:
    Banyak developer pemula mematikan middleware VerifyCsrfToken di Laravel hanya karena merasa kerepotan saat melakukan pengujian API via Postman. Ini adalah praktek yang sangat berbahaya jika aplikasi menggunakan autentikasi berbasis cookie session.
  • Menyimpan Access Token di localStorage Tanpa Memahami Risikonya:
    Beberapa developer menghindari CSRF dengan menyimpan JWT di localStorage daripada cookie. Meskipun ini membebaskan aplikasi dari CSRF, hal ini membuat aplikasi kamu sangat rentan terhadap XSS. Setiap skrip pihak ketiga atau dependensi npm yang terinfeksi dapat mencuri token dari localStorage.

Penutup

Serangan Cross-Site Request Forgery (CSRF) memanfaatkan perilaku alami browser yang menyertakan cookie kredensial secara otomatis pada setiap request ke target host. Walaupun konsep serangan ini tampak sederhana, dampaknya bisa sangat fatal mulai dari pengambilalihan akun hingga kerugian finansial.

Mencegah CSRF memerlukan pemahaman yang jelas antara peran Anti-CSRF Token (Synchronizer / Double-Submit) dan penggunaan atribut cookie modern SameSite. Dengan menerapkan fitur keamanan bawaan framework seperti Laravel @csrf atau mengonfigurasi middleware yang tepat di Node.js, aplikasi web kamu akan terlindungi dari ancaman ini secara efektif.

Referensi terkait: OWASP CSRF Prevention Cheat Sheet, RFC 6265bis SameSite Specification, Laravel CSRF Protection, MDN SameSite Cookies.

Jika kamu ingin mendalami topik keamanan web terkait, baca artikel tentang XSS dan Cara Mencegahnya, SQL Injection dan Cara Mencegahnya, dan Cara Mengamankan REST API. Untuk pemahaman tentang otentikasi yang aman di Laravel, baca Password Hashing.

Bagikan Artikel:
Diskusi & Komentar

Fitur komentar belum diaktifkan oleh administrator.

Artikel Terkait