Secure AJAX Handlers with Nonce and Capability Checks
Share
WordPress AJAX handlers are public endpoints. wp_ajax_ fires for any logged-in user regardless of role. Always verify nonces and capabilities inside each handler. [CWE-862 · A01:2021]
Why This Matters
prevents unauthorized data access and modification through AJAX endpoints
Secure AJAX Handlers with Nonce and Capability Checks
Impact: CRITICAL (prevents unauthorized data access and modification through AJAX endpoints)
WordPress AJAX handlers registered via wp_ajax_{action} fire for any logged-in user — subscribers, contributors, authors, editors, and admins alike. A subscriber can call any wp_ajax_ handler. wp_ajax_nopriv_{action} fires for unauthenticated visitors. Without explicit nonce and capability checks, these are open endpoints.
CVE-2024-9061 (WP Popup Builder) demonstrated this: a wp_ajax_nopriv_ handler executed arbitrary shortcodes for unauthenticated users because it had no authorization checks.
Incorrect (no security checks):
// ❌ Any logged-in user (including subscribers) can delete postsadd_action( 'wp_ajax_delete_post', function() { $post_id = $_POST['post_id']; wp_delete_post( $post_id, true ); wp_send_json_success();});// ❌ Unauthenticated users can read private dataadd_action( 'wp_ajax_nopriv_get_user_data', function() { $user_id = $_POST['user_id']; $user = get_userdata( $user_id ); wp_send_json( [ 'email' => $user->user_email, 'name' => $user->display_name, ]); // Leaks PII to anyone});// ❌ Nonce checked but no capability check — subscriber can still trigger thisadd_action( 'wp_ajax_update_settings', function() { check_ajax_referer( 'settings_nonce', 'nonce' ); update_option( 'my_setting', $_POST['value'] ); // Any role can change settings! wp_send_json_success();});