Bali, Indonesia
Insights

Mengira Ganti Password Admin Cukup Setelah WordPress Dibobol

October 23, 2026
0:00
Setelah WordPress dibobol, langkah pertama yang hampir selalu diambil adalah mengganti password admin. Itu wajar, karena form login adalah pintu yang paling kelihatan. Masalahnya, beberapa hari kemudian post spam muncul lagi, plugin asing terpasang lagi, dan kesimpulannya jadi passwordnya kurang kuat. Biasanya bukan itu penyebabnya. Penyusup memang sudah tidak butuh password kamu. Tulisan ini disusun seperti post-mortem dari pola yang sering muncul saat menangani situs WordPress yang dibobol. Ini bukan catatan satu kejadian spesifik. Tujuannya supaya kamu paham kenapa ganti password terasa seperti solusi, padahal yang tertutup cuma satu dari beberapa jalur masuk.
Gejalanya biasanya mirip. Ada konten yang tidak pernah dibuat pemilik situs, redirect ke situs lain, atau plugin dengan nama generik yang tiba-tiba aktif. Pemilik situs ganti password admin, mungkin juga hapus plugin yang mencurigakan, lalu semuanya terlihat normal. Lalu gejalanya kembali. Di sisi support, permintaan pertama yang paling sering masuk adalah tolong reset password admin. Permintaan itu sah, tapi kalau berhenti di situ, tiketnya hampir pasti dibuka lagi. Penyebabnya adalah cara WordPress mengautentikasi request. Form wp-login.php hanya satu cara. Ada cookie login yang sudah diterbitkan, ada kredensial terpisah untuk API, ada akun lain di tabel user, dan ada kunci rahasia di wp-config.php yang dipakai untuk menandatangani cookie. Ganti password satu akun tidak menyentuh sebagian besar dari itu.
Investigasi yang masuk akal biasanya berjalan dalam beberapa tahap. Tahap pertama, cek apakah penyusup masih login lewat form. Di access log web server, cari POST /wp-login.php dari IP yang tidak dikenal. Kalau ternyata tidak ada sama sekali tapi perubahan tetap terjadi, berarti penyusup masuk lewat jalur lain. Tahap kedua, lihat request ke /xmlrpc.php dan /wp-json/. Request ke REST API yang mengubah data, misalnya POST ke endpoint post atau plugin dari IP asing tanpa didahului login, adalah tanda kuat ada kredensial API yang masih hidup. Access log biasanya tidak mencatat header Authorization, jadi kamu hanya melihat polanya, bukan kredensialnya. Tahap ketiga, bandingkan daftar user di dashboard dengan isi database. Di sinilah temuan paling penting sering muncul. Kode berbahaya bisa memfilter query user sehingga akun tertentu tidak tampil di halaman Users. Karena itu, jangan percaya tampilan dashboard. WP-CLI dengan plugin dan tema dimatikan lebih bisa diandalkan:
BASH
wp user list --role=administrator --skip-plugins --skip-themes --fields=ID,user_login,user_email,user_registered
Acuan paling jujur tetap database itu sendiri, karena query langsung tidak melewati kode PHP apa pun. Sesuaikan prefix wp_ dengan $table_prefix di wp-config.php. Meta key-nya juga ikut prefix, misalnya abc_capabilities.
SQL
SELECT u.ID, u.user_login, u.user_email, u.user_registered
FROM wp_users u
JOIN wp_usermeta m ON m.user_id = u.ID
WHERE m.meta_key = 'wp_capabilities'
  AND m.meta_value LIKE '%administrator%';
Perhatikan juga akun lama yang terlihat wajar. Penyusup tidak selalu membuat akun baru. Kadang mereka menaikkan role akun subscriber jadi administrator, atau mengganti email akun admin yang sah supaya link reset password terkirim ke mereka.
Kalau dirangkum, ada empat jalur yang tetap terbuka walaupun password admin utama sudah diganti. Pertama, akun admin lain. Ini paling sederhana tapi paling sering terlewat. Akun admin tambahan punya password sendiri, cookie sendiri, dan semuanya tidak terpengaruh perubahan di akun kamu. Kedua, Application Passwords. Fitur ini ada di core WordPress sejak versi 5.6. Fungsinya membuat password khusus per aplikasi untuk REST API dan XML-RPC dengan Basic Auth. Password ini tidak bisa dipakai login ke wp-admin, tapi lewat API, ia bisa melakukan hampir semua yang boleh dilakukan user pemiliknya. Kalau user itu administrator, cakupannya luas. Application Password tersimpan terpisah dari password utama, jadi biasanya tidak ikut terhapus saat password user diganti. Penyusup yang pernah punya akses admin cukup membuat satu Application Password dengan nama yang tidak mencurigakan, lalu kembali kapan saja lewat API. Ketiga, session aktif. Setelah login, WordPress menerbitkan cookie dan mencatat session token di user meta session_tokens. Validasi cookie melibatkan potongan hash password user. Artinya, saat password akun A diganti, cookie milik akun A ikut tidak berlaku. Tapi cookie akun B, termasuk akun admin tersembunyi tadi, tetap valid sampai kedaluwarsa. Secara default masa berlakunya sekitar dua hari, atau sekitar empat belas hari kalau Remember Me dicentang. Keempat, salt dan key di wp-config.php. Delapan konstanta seperti AUTH_KEY, LOGGED_IN_KEY, dan pasangan *_SALT dipakai untuk menandatangani cookie. Kalau penyusup pernah bisa membaca file di server, anggap wp-config.php sudah terbaca. Di file yang sama ada kredensial database. Dengan salt plus akses ke hash password di database, cookie yang valid bisa dibuat tanpa pernah login. Dengan kredensial database, akun admin baru bisa disisipkan langsung lewat SQL, apalagi kalau remote MySQL terbuka. Temuan ini mengubah cara kita melihat insiden. Password admin bukan satu-satunya kunci. Yang bocor bisa jadi seluruh rantai kepercayaan situs.
Sebelum mengubah apa pun, simpan salinan kondisi saat ini, yaitu file dan dump database, di luar document root. Salinan ini dibutuhkan untuk analisis dan untuk mencari titik restore yang bersih nanti. Setelah itu, prioritasnya menghentikan akses secepat mungkin, walaupun belum rapi. Beberapa langkah yang biasanya cukup efektif:
  • Batasi akses ke /wp-admin/, wp-login.php, dan xmlrpc.php di level web server, misalnya allow per IP atau basic auth tambahan. Caranya tergantung Nginx, Apache, LiteSpeed, atau panel yang dipakai.
  • Matikan Application Passwords sementara dengan mu-plugin kecil berisi add_filter( 'wp_is_application_passwords_available', '__return_false' );. Ini menutup jalur API berbasis Application Password tanpa menunggu audit per user selesai.
  • Rotasi salt. Ini cara tercepat untuk membatalkan semua cookie login semua user sekaligus.
Mitigasi ini sengaja kasar. Tujuannya membeli waktu, bukan menyelesaikan masalah. Selama backdoor di file masih ada, penyusup bisa membuat akun dan kredensial baru lagi.
Urutan penting di sini. Kalau kredensial dirotasi sementara backdoor masih aktif, kredensial baru bisa ikut dicuri. Urutan yang aman kurang lebih seperti ini:
  1. Bersihkan jalur masuknya dulu. Cari file PHP asing, terutama di wp-content/mu-plugins/, plugin dan tema yang tidak dikenal, serta file core yang berubah. Update atau hapus plugin yang rentan. Kalau pembersihan tidak meyakinkan, restore dari backup yang jelas bersih.
  2. Audit user dari database. Hapus akun admin yang tidak dikenal, dan pindahkan kontennya ke akun yang sah saat menghapus. Kembalikan role dan email akun lama yang diubah.
  3. Cabut semua Application Password di setiap akun dengan role tinggi, bukan hanya akun utama.
  4. Ganti password semua admin, password database (lalu update DB_PASSWORD di wp-config.php), serta akses hosting, FTP, dan SSH. Cek juga apakah ada SSH key asing di server.
  5. Rotasi salt paling akhir supaya semua session yang tersisa terputus, termasuk session yang mungkin dibuat selama proses pembersihan.
Untuk langkah 3 sampai 5, WP-CLI memangkas banyak klik. Contoh di bawah untuk user ID 1, jadi ulangi untuk tiap admin. Perhatikan bahwa shuffle-salts menulis ulang wp-config.php dan membuat semua user logout. Backup file itu dulu.
BASH
# lihat Application Password milik user ID 1, termasuk kapan terakhir dipakai
wp user application-password list 1 --skip-plugins --skip-themes

# hapus semua Application Password milik user tersebut
wp user application-password delete 1 --all --skip-plugins --skip-themes

# putus semua session login user tersebut
wp user session destroy 1 --all --skip-plugins --skip-themes

# ganti semua key dan salt di wp-config.php (semua user akan logout)
wp config shuffle-salts
Kolom seperti waktu terakhir dipakai dan IP terakhir di daftar Application Password berguna untuk menyusun timeline insiden. Kalau ada entri yang terakhir dipakai dari IP asing, itu konfirmasi jalur yang selama ini dipakai penyusup. Setelah semua beres, mu-plugin yang mematikan Application Passwords boleh dilepas kalau fitur itu memang dibutuhkan. Kalau tidak dibutuhkan, membiarkannya mati adalah trade-off yang wajar. Pegangan praktisnya begini. Setelah WordPress dibobol, anggap semua yang pernah bisa dibaca penyusup sudah bocor, bukan cuma password admin. Tutup backdoor-nya, audit user langsung dari database, cabut Application Passwords, ganti password termasuk database, lalu rotasi salt di akhir. Kalau kelima langkah itu dijalankan berurutan, kecil kemungkinan kamu harus membersihkan situs yang sama dua kali.