Website yang menerima data dari pengguna biasanya sudah memakai POST untuk menyimpan perubahan. Namun, mengganti GET menjadi POST belum otomatis membuatnya aman. Jika pengguna sedang login, browser akan mengirim cookie sesi secara otomatis—termasuk ketika permintaan dipicu dari situs lain.
Di sinilah Cross-Site Request Forgery atau CSRF bisa terjadi. Sederhananya, pengguna memang tidak memberikan perintah secara sadar, tetapi browsernya membantu mengirim permintaan yang terlihat sah ke website tempat ia sedang login. Dampaknya bisa berupa perubahan email, pergantian password, penghapusan data, atau transaksi yang tidak diinginkan. OWASP menjelaskan bahwa serangan ini memanfaatkan cara browser mengirim cookie autentikasi secara otomatis. ([cheatsheetseries.owasp.org](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html?utm_source=openai))
Contoh sederhana serangan CSRF
Bayangkan ada endpoint seperti /ubah-email.php yang hanya memeriksa apakah pengguna sudah login:
<form action="https://contoh.com/ubah-email.php" method="POST">
<input type="hidden" name="email" value="penyerang@example.com">
</form>Form tersebut dapat ditempatkan di situs lain dan dikirim otomatis saat halaman dibuka. Jika korban masih memiliki sesi aktif di contoh.com, browser mungkin ikut mengirim cookie sesi. Server lalu mengira permintaan itu benar-benar berasal dari pengguna.
Serangan seperti ini tidak membutuhkan pencurian password. Penyerang hanya berusaha membuat browser korban mengirim permintaan ke website yang sudah dipercaya.
Jangan mengandalkan POST, HTTPS, atau Referer saja
Beberapa asumsi keamanan sering terdengar masuk akal, tetapi tidak cukup sebagai perlindungan utama.
- POST bukan pencegah CSRF. Form HTML dari situs lain tetap bisa mengirim permintaan POST.
- HTTPS bukan pencegah CSRF. HTTPS melindungi koneksi, tetapi tidak membuktikan bahwa pengguna memang bermaksud melakukan aksi tersebut.
- Pemeriksaan Referer saja kurang kuat. Header ini bisa tidak dikirim karena kebijakan privasi, proxy, atau konfigurasi browser tertentu.
- Meminta konfirmasi pada langkah kedua belum tentu cukup. Jika semua langkah dapat ditebak dan tidak memiliki perlindungan tambahan, serangan masih mungkin dilakukan.
HTTPS tetap wajib digunakan, tetapi posisinya adalah fondasi keamanan, bukan pengganti token CSRF. OWASP juga menyarankan agar operasi yang mengubah data tidak dilakukan melalui GET. ([community.owasp.org](https://community.owasp.org/attacks/csrf?utm_source=openai))
Pola utama: token CSRF di server
Pendekatan yang umum untuk aplikasi PHP berbasis sesi adalah synchronizer token. Server membuat token acak, menyimpannya di sesi pengguna, lalu menyisipkannya ke form. Saat form dikirim, server membandingkan token dari form dengan token di sesi.
Token harus dibuat dengan sumber acak yang aman dan tidak boleh ditebak. Di PHP, contoh sederhananya dapat menggunakan random_bytes():
<?php
session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$csrfToken = $_SESSION['csrf_token'];
?>
<form method="post" action="simpan-profil.php">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrfToken, ENT_QUOTES, 'UTF-8') ?>">
<button type="submit">Simpan</button>
</form>Pada endpoint penerima, jangan hanya memeriksa apakah field tersebut ada. Bandingkan nilainya dengan token di sesi menggunakan hash_equals() agar perbandingan tidak mudah dibaca melalui perbedaan waktu eksekusi:
<?php
session_start();
$token = $_POST['csrf_token'] ?? '';
$sessionToken = $_SESSION['csrf_token'] ?? '';
if (!$sessionToken || !$token || !hash_equals($sessionToken, $token)) {
http_response_code(403);
exit('Permintaan tidak valid.');
}
// Lanjutkan validasi input dan proses penyimpanan.
Token bukan pengganti autentikasi dan otorisasi. Setelah token valid, aplikasi tetap harus memeriksa apakah pengguna memiliki hak untuk mengubah data tersebut.
Bagaimana dengan WordPress?
WordPress menyediakan fitur bernama nonce untuk membantu melindungi form dan URL dari penyalahgunaan, termasuk CSRF. Namun, istilah “nonce” di WordPress tidak berarti token yang benar-benar hanya dapat dipakai sekali. Token memiliki masa berlaku dan konteks tertentu.
Contoh pembuatan field nonce di form plugin:
<?php wp_nonce_field('simpan_pengaturan', 'pengaturan_nonce'); ?>Di handler, token perlu diverifikasi:
<?php
if (
!isset($_POST['pengaturan_nonce']) ||
!wp_verify_nonce($_POST['pengaturan_nonce'], 'simpan_pengaturan')
) {
wp_die('Permintaan tidak valid.');
}
if (!current_user_can('manage_options')) {
wp_die('Anda tidak memiliki izin.');
}
Bagian pentingnya adalah wp_verify_nonce() dan current_user_can() memiliki fungsi berbeda. Nonce membantu memastikan konteks permintaan, sedangkan pemeriksaan kemampuan menentukan apakah pengguna boleh melakukan aksi tersebut. Dokumentasi WordPress secara eksplisit mengingatkan agar nonce tidak dijadikan pengganti autentikasi atau kontrol akses. ([developer.wordpress.org](https://developer.wordpress.org/apis/security/nonces/?utm_source=openai))
Tambahkan perlindungan pada cookie sesi
Token CSRF tetap menjadi pertahanan utama, tetapi atribut cookie dapat membantu mengurangi risiko. Cookie sesi sebaiknya menggunakan Secure agar hanya dikirim melalui HTTPS, HttpOnly agar tidak dibaca JavaScript, dan SameSite=Lax atau Strict sesuai kebutuhan alur aplikasi.
<?php
session_set_cookie_params([
'secure' => true,
'httponly' => true,
'samesite' => 'Lax'
]);
session_start();
PHP mendukung pengaturan Secure, HttpOnly, dan SameSite melalui session_set_cookie_params(). Namun, SameSite sebaiknya dianggap sebagai lapisan tambahan, bukan alasan untuk menghapus token CSRF. Perilaku Lax, misalnya, masih mengizinkan cookie pada beberapa navigasi lintas situs. ([php.net](https://www.php.net/manual/en/function.session-set-cookie-params.php?utm_source=openai))
Apa artinya bagi kita?
CSRF sering muncul bukan karena kode terlihat rumit, melainkan karena endpoint dibuat terlalu percaya pada cookie sesi. Setiap aksi yang mengubah keadaan—mengedit profil, mengganti password, menghapus konten, membuat pesanan, atau mengubah pengaturan—perlu diperlakukan sebagai operasi sensitif.
Yang bisa dilakukan sekarang
- Daftar semua endpoint yang mengubah data dan pastikan tidak menggunakan
GET. - Tambahkan token CSRF pada setiap form atau permintaan AJAX yang memakai autentikasi berbasis cookie.
- Validasi token di server, bukan hanya melalui JavaScript.
- Gunakan pemeriksaan kemampuan atau peran setelah token dinyatakan valid.
- Aktifkan atribut cookie
Secure,HttpOnly, danSameSitesesuai arsitektur aplikasi. - Tolak token yang hilang atau tidak cocok dengan respons
403, lalu catat kejadian tersebut tanpa menyimpan token ke log. - Uji alur penting dari domain berbeda untuk memastikan permintaan tanpa token benar-benar ditolak.
Perlindungan CSRF tidak harus dimulai dari sistem yang besar. Satu token acak, satu pemeriksaan server-side, dan satu pengecekan izin sudah jauh lebih baik daripada hanya mengandalkan metode POST atau ikon gembok di browser.
Sumber & bacaan lebih lanjut
- OWASP Cross-Site Request Forgery Prevention Cheat Sheet
- OWASP Cross-Site Request Forgery (CSRF)
- WordPress Developer Handbook: Nonces
- PHP Manual: session_set_cookie_params
- PHP Manual: Session Management Basics
– Rio Yotto @rioyotto
