Home / Artikel / Web Security
Web Security

CSRF di PHP dan WordPress: Ketika Browser Menjalankan Aksi Tanpa Anda Sadari

CSRF memungkinkan situs lain memancing browser pengguna yang sedang login untuk mengirim permintaan ke website tepercaya. Kenali cara kerjanya dan pasang perlindungan yang tepat di PHP maupun WordPress.

CSRF di PHP dan WordPress: Ketika Browser Menjalankan Aksi Tanpa Anda Sadari

Bayangkan Anda sedang login ke dashboard website, lalu membuka tab lain yang berisi halaman berbahaya. Halaman itu tidak perlu mengetahui password Anda. Jika website tidak memiliki perlindungan yang memadai, browser dapat ikut mengirim cookie login ketika halaman tersebut mencoba menjalankan aksi seperti mengganti email, menghapus data, atau mengubah pengaturan akun.

Inilah inti Cross-Site Request Forgery atau CSRF: situs lain memanfaatkan status login yang sudah aktif untuk mengirim permintaan palsu ke website tepercaya. Serangannya tidak selalu terlihat. Korban mungkin hanya merasa sedang membuka halaman biasa, sementara browser bekerja menggunakan hak akses yang sudah dimilikinya.

CSRF berbeda dari XSS

CSRF dan XSS sering disebut bersamaan, tetapi masalahnya berbeda. Pada XSS, penyerang berusaha menjalankan JavaScript berbahaya di dalam halaman website target. Pada CSRF, penyerang memanfaatkan browser korban untuk mengirim permintaan yang tampak sah ke website target.

CSRF biasanya menjadi risiko ketika aplikasi menggunakan cookie untuk autentikasi. Browser memang dirancang untuk mengirim cookie secara otomatis ke domain yang sesuai. Kenyamanan ini juga yang dapat dimanfaatkan jika server tidak meminta bukti tambahan bahwa permintaan benar-benar berasal dari halaman website sendiri.

Risikonya bergantung pada aksi yang tersedia. Pada website sederhana, dampaknya mungkin berupa perubahan profil. Pada aplikasi bisnis, CSRF dapat berhubungan dengan perubahan rekening pembayaran, pembuatan pengguna baru, penghapusan konten, atau perubahan hak akses.

Jangan gunakan GET untuk mengubah data

Langkah pertama yang sering dilupakan adalah memisahkan permintaan untuk membaca data dan mengubah data. URL seperti /delete-user?id=25 berbahaya jika hanya dengan mengunjunginya saja data langsung terhapus.

Permintaan GET seharusnya bersifat aman dan tidak mengubah keadaan aplikasi. Gunakan POST, PUT, PATCH, atau DELETE untuk aksi yang mengubah data. Namun, mengganti GET menjadi POST saja belum cukup. Form POST tetap dapat dikirim oleh halaman dari domain lain, sehingga tetap membutuhkan perlindungan CSRF.

Pola token pada aplikasi PHP

Pendekatan yang umum adalah synchronizer token. Server membuat token acak yang terkait dengan session pengguna. Token itu dimasukkan ke dalam form. Saat form dikirim, server membandingkan token dari request dengan token yang tersimpan di session.

Token harus dibuat menggunakan sumber acak yang aman dan tidak boleh mudah ditebak. Contoh sederhana berikut menunjukkan polanya:

<?php
session_start();

if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

$token = $_SESSION['csrf_token'];
?>

<form method="post" action="/ubah-profil.php">
    <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($token, ENT_QUOTES, 'UTF-8') ?>">
    <button type="submit">Simpan</button>
</form>

Di sisi pemrosesan, jangan langsung mempercayai nilai yang diterima:

<?php
session_start();

$token = $_POST['csrf_token'] ?? '';
$sessionToken = $_SESSION['csrf_token'] ?? '';

if (!$sessionToken || !hash_equals($sessionToken, $token)) {
    http_response_code(403);
    exit('Permintaan tidak valid.');
}

// Lanjutkan validasi input dan proses perubahan data.
?>

Catatan penting: token CSRF bukan pengganti autentikasi dan otorisasi. Setelah token lolos, aplikasi tetap harus memeriksa apakah pengguna boleh mengubah data tersebut. Jangan sampai pengguna biasa dapat mengubah profil milik orang lain hanya karena tokennya valid.

Memakai nonce dengan benar di WordPress

WordPress menyediakan mekanisme nonce untuk membantu mencegah CSRF pada form, URL, dan AJAX. Untuk form, gunakan wp_nonce_field() saat menampilkan form, lalu validasi dengan check_admin_referer() atau wp_verify_nonce() ketika request diproses.

<form method="post">
    <?php wp_nonce_field('simpan_pengaturan', 'pengaturan_nonce'); ?>
    <input type="text" name="nama_situs">
    <button type="submit">Simpan</button>
</form>
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    check_admin_referer('simpan_pengaturan', 'pengaturan_nonce');

    if (!current_user_can('manage_options')) {
        wp_die('Anda tidak memiliki izin.');
    }

    // Sanitasi input, lalu simpan perubahan.
}
?>

Urutannya penting. Nonce memeriksa apakah request memiliki tanda yang diharapkan, sedangkan current_user_can() memeriksa hak akses. Keduanya menjawab pertanyaan yang berbeda. WordPress sendiri menegaskan bahwa nonce tidak boleh dijadikan pengganti autentikasi, otorisasi, atau pemeriksaan capability.

Lapisan perlindungan tambahan

Token sebaiknya menjadi pertahanan utama, bukan satu-satunya lapisan. Atribut cookie SameSite dapat membantu membatasi pengiriman cookie pada konteks lintas situs. Untuk session yang sensitif, gunakan konfigurasi cookie yang sesuai seperti Secure dan HttpOnly, serta pastikan seluruh website berjalan melalui HTTPS.

Server juga dapat memeriksa header Origin atau Referer sebagai pemeriksaan tambahan. Pada aplikasi modern, header Fetch Metadata seperti Sec-Fetch-Site juga dapat membantu mendeteksi request lintas situs. Namun, jangan menggantungkan keamanan hanya pada satu header karena proxy, browser lama, atau konfigurasi tertentu dapat membuat header tidak tersedia.

Jika aplikasi memiliki operasi yang sangat sensitif, tambahkan konfirmasi ulang. Misalnya, meminta password kembali, kode autentikasi tambahan, atau konfirmasi eksplisit sebelum mengubah rekening pembayaran dan menghapus akun.

Yang bisa dilakukan sekarang

  • Inventarisasi semua endpoint yang mengubah data, termasuk endpoint AJAX dan REST API.
  • Pastikan tidak ada aksi perubahan data yang dipicu oleh GET.
  • Tambahkan token CSRF pada seluruh form dan request yang mengubah keadaan aplikasi.
  • Validasi token di server sebelum melakukan query atau perubahan data.
  • Gunakan pemeriksaan capability atau role secara terpisah dari token.
  • Periksa kembali plugin WordPress yang membuat form, tombol hapus, atau endpoint AJAX sendiri.
  • Catat request dengan token tidak valid agar pola serangan dan kesalahan integrasi dapat diketahui.

CSRF bukan masalah yang hanya muncul pada aplikasi besar. Form pengaturan sederhana, tombol hapus, dan endpoint AJAX buatan sendiri sudah cukup untuk menciptakan celah. Kabar baiknya, perlindungannya juga tidak harus rumit: gunakan metode HTTP yang tepat, pasang token yang sulit ditebak, validasi di server, dan jangan lupa memeriksa hak akses pengguna.

Sumber & bacaan lebih lanjut

Jelajahi juga

– Rio Yotto @rioyotto