Wordpress Security Best Practices Nonces Capabilities Escaping Sanitization Safe Output
Selesai membaca dokumentasi ini?
Kembali ke rujukan utama WordPress untuk melanjutkan topik lainnya.
Kembali ke rujukan utama WordPress untuk melanjutkan topik lainnya.
Di WordPress, keamanan input-output tidak diselesaikan oleh satu fungsi tunggal. Kamu perlu memisahkan beberapa lapisan:
Kalau kelimanya dipakai dengan benar, plugin/theme biasanya jauh lebih tahan terhadap input berbahaya, request palsu, dan output yang bocor ke halaman.
Pakai capabilities saat kamu perlu menjawab pertanyaan: “Siapa yang boleh melakukan aksi ini?”
Contoh:
Capabilities dicek dengan fungsi seperti current_user_can().
if ( ! current_user_can('manage_options') ) {
wp_die('You are not allowed to access this page.');
}Capability itu bukan sekadar tampilan menu. Menu bisa disembunyikan, tapi callback, endpoint, dan save handler tetap harus dicek lagi.
Pakai nonces saat request berasal dari browser dan berpotensi dipalsukan oleh situs lain, terutama untuk:
Nonce WordPress dipakai untuk memverifikasi bahwa request memang berasal dari konteks yang kamu harapkan, bukan submit acak dari luar.
Contoh form:
wp_nonce_field('myplugin_save_settings', 'myplugin_nonce');Contoh verifikasi:
if ( ! isset($_POST['myplugin_nonce']) ||
! wp_verify_nonce($_POST['myplugin_nonce'], 'myplugin_save_settings') ) {
wp_die('Invalid request.');
}Nonce bukan pengganti capability. Nonce menjawab “apakah request ini valid dari browser yang diharapkan?”, bukan “apakah user berhak?”.
Pakai sanitization saat data masuk ke sistem dan akan disimpan atau diproses lebih lanjut.
Contoh:
sanitize_text_field()sanitize_textarea_field()sanitize_email()esc_url_raw() atau sanitize_url() tergantung konteks dan versi WordPresswp_kses_post() atau wp_kses() dengan allowlistSanitization mencegah data kotor masuk ke database, option, meta, atau transient.
Pakai escaping saat data keluar ke template, admin page, email HTML, attribute, inline JS, atau URL.
Contoh umum:
esc_html()esc_attr()esc_url()esc_textarea()wp_json_encode() dan pola output yang amanAturan singkatnya: sanitize on input, escape on output.
Pakai safe output ketika data dari database, API, user input, atau metadata ditampilkan di:
Jangan pernah menganggap data dari database sudah aman hanya karena pernah disimpan. Data tersimpan tetap harus di-escape sesuai konteks render.
Urutan yang paling aman biasanya begini:
Untuk form admin yang menyimpan state, gunakan tiga lapisan sekaligus:
Contoh pola:
function myplugin_render_page() {
if ( ! current_user_can('manage_options') ) {
wp_die('You are not allowed to access this page.');
}
?>
<form method="post" action=
Lalu saat submit:
function myplugin_handle_save() {
if ( ! current_user_can('manage_options') ) {
wp_die('You are not allowed to perform this action.');
}
if ( ! isset($_POST['myplugin_nonce']) ||
!
Kalau aksi mengubah data lewat link atau button, nonce tetap perlu. Biasanya bentuknya seperti:
$url = wp_nonce_url(
admin_url('admin-post.php?action=myplugin_delete_cache'),
'myplugin_delete_cache'
);Di handler:
if ( ! current_user_can('manage_options') ) {
wp_die('Forbidden');
}
check_admin_referer('myplugin_delete_cache');Untuk AJAX, pola amannya serupa:
check_ajax_referer()add_action('wp_ajax_myplugin_save', function () {
if ( ! current_user_can('edit_posts') ) {
wp_send_json_error(['message' => 'Forbidden'], 403);
}
check_ajax_referer('myplugin_save', 'nonce'
Admin UI sering terasa “aman” karena hanya bisa diakses user login, padahal tetap rawan XSS dari data yang tersimpan.
esc_html()esc_attr()esc_url()esc_textarea()wp_kses_post() atau wp_kses()Contoh:
echo '<h1>' . esc_html($page_title) . '</h1>';
echo '<a href="' . esc_url($settings_url) . '">Settings</a>';
echo '<input value="' . esc_attr($saved_value) . '">';$_POST langsung setelah submitecho $option tanpa escape karena datanya “dari admin”wp_kses_post() untuk semua output padahal kadang terlalu longgarselected(), checked(), disabled() jika dipakai dalam markup customFrontend sering lebih berbahaya karena user publik bisa melihat hasilnya langsung.
Contoh shortcode:
function myplugin_box_shortcode($atts) {
$atts = shortcode_atts([
'title' => '',
], $atts, 'myplugin_box');
$title = sanitize_text_field($atts['title']);
return '<div class="box"><h3>' . esc_html($title) .
Catatan penting: atribut shortcode boleh disanitasi saat diproses, tetapi tetap di-escape saat dirender.
Ini salah satu jebakan paling umum.
Dipakai untuk membersihkan data sebelum disimpan atau diproses.
Tujuan:
Dipakai untuk mengamankan data ketika ditampilkan.
Tujuan:
Nonce bukan izin. User tetap harus lolos capability check.
wp_unslash() saat baca $_POSTWordPress menambahkan slashing pada superglobals. Data input biasanya perlu di-wp_unslash() sebelum disanitasi.
$value = sanitize_text_field(wp_unslash($_POST['value'] ?? ''));Contoh:
sanitize_text_field() sehingga markup yang memang diizinkan ikut hilangesc_attr()Data lama, data migrasi, dan data impor bisa saja kotor. Database bukan zona aman otomatis.
Template dan admin page sering bocor XSS karena developer fokus pada layout, bukan konteks output.
Satu nilai bisa butuh escaping yang berbeda tergantung tempatnya:
Kalau admin-only action diberi capability yang terlalu umum, permukaan risiko jadi melebar.
Pilih capability yang sesuai dengan dampak aksinya, bukan sekadar yang paling gampang bekerja.
Pola ringkas yang sehat untuk banyak kasus plugin/theme:
function myplugin_save_setting() {
if ( ! current_user_can('manage_options') ) {
wp_die('Forbidden');
}
check_admin_referer('myplugin_save_setting');
$title = sanitize_text_field(wp_unslash
Intinya:
Kalau kamu membangun plugin/theme WordPress, biasakan satu pertanyaan untuk tiap layer: siapa yang boleh, request ini valid atau tidak, datanya aman disimpan atau tidak, dan output ini harus di-escape dengan cara apa.