Wordpress Security Validation
Security-first WordPress development with nonces, sanitization, validation, and escaping to prevent XSS, CSRF, and SQL injection vulnerabilities.
- Skill ID
- bobmatnyc/claude-mpm-skills/wordpress-security-validation
- Publisher
- bobmatnyc
- Repository
- claude-mpm-skills
- Installs
- 0
- Files
- 3
- Synced
- Sep 23, 2026
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
bobmatnyc/claude-mpm-skills/wordpress-security-validationInstalls these files- SKILL.md
- metadata.json
- references/php-quality-antipatterns.md
What this skill tells the agent
WordPress Security & Data Validation
Version: 1.1.0 Target: WordPress 6.7+ | PHP 8.3+ Skill Level: Intermediate to Advanced
Overview
Security is not optional in WordPress development—it's fundamental. This skill teaches the three-layer security model that prevents XSS, CSRF, SQL injection, and other common web vulnerabilities through proper input sanitization, business logic validation, and output escaping.
The Golden Rule: "Sanitize on input, validate for logic, escape on output."
Why This Matters
Every year, thousands of WordPress sites are compromised due to security vulnerabilities in plugins and themes. Most of these attacks exploit one of three weaknesses:
- XSS (Cross-Site Scripting): Malicious JavaScript injected through unsanitized output
- CSRF (Cross-Site Request Forgery): Unauthorized actions performed on behalf of authenticated users
- SQL Injection: Database manipulation through unsanitized database queries
This skill provides complete, production-ready patterns for preventing all three attack vectors.
The Three-Layer Security Model
WordPress security follows a defense-in-depth strategy with three distinct layers:
User Input → [1. SANITIZE] → [2. VALIDATE] → Process → [3. ESCAPE] → OutputLayer 1: Sanitization (Input Cleaning)
Purpose: Remove dangerous characters and normalize data format When: Immediately upon receiving user input Example: sanitize_text_field($_POST['username'])
Layer 2: Validation (Logic Checks)
Purpose: Ensure data meets business requirements When: After sanitization, before processing Example: if (!is_email($email)) { /* error */ }
Layer 3: Escaping (Output Protection)
Purpose: Prevent XSS by encoding special characters When: Every time you output data to browser Example: echo esc_html($user_input);
Critical Distinction:
- Sanitization removes/transforms invalid data (changes the value)
- Validation checks if data is acceptable (returns true/false)
- Escaping makes data safe for display (context-specific encoding)
1. Nonces: CSRF Protection
What Are Nonces?
Nonces (Numbers Used Once) are cryptographic tokens that verify a request originated from your site, not a malicious external source. They prevent Cross-Site Request Forgery (CSRF) attacks.
How CSRF Attacks Work:
<!-- Attacker's malicious site: evil.com -->
<img src="https://yoursite.com/wp-admin/admin.php?action=delete_user&id=1">
<!-- If user is logged into yoursite.com, this executes! -->How Nonces Prevent CSRF:
<!-- Legitimate request with nonce -->
<form action="admin.php?action=delete_user&id=1" method="POST">
<?php wp_nonce_field('delete_user_1', 'delete_nonce'); ?>
<button>Delete User</button>
</form>
<!-- Attacker cannot generate valid nonce (tied to user session) -->Nonce Implementation Patterns
Pattern 1: Form Nonces (Most Common)
BEFORE (Vulnerable):
// Vulnerable form processing
if (isset($_POST['submit'])) {
$user_id = absint($_POST['user_id']);
delete_user($user_id); // ⚠️ CSRF vulnerable!
}AFTER (Secure):
// Generate nonce in form
<form method="post" action="">
<?php wp_nonce_field('delete_user_action', 'delete_user_nonce'); ?>
<input type="hidden" name="user_id" value="42">
<button type="submit" name="submit">Delete User</button>
</form>
// Verify nonce on submission
if (isset($_POST['submit'])) {
// Security check #1: Verify nonce
if (!isset($_POST['delete_user_nonce']) ||
!wp_verify_nonce($_POST['delete_user_nonce'], 'delete_user_action')) {
wp_die('Security check failed: Invalid nonce');
}
// Security check #2: Capability check
if (!current_user_can('delete_users')) {
wp_die('You do not have permission to delete users');
}
// Now safe to process
$user_id = absint($_POST['user_id']);
wp_delete_user($user_id);
}Key Functions:
wp_nonce_field($action, $name)- Generates hidden nonce fieldwp_verify_nonce($nonce, $action)- Verifies nonce validity
Pattern 2: URL Nonces
Use Case: Delete/trash links, admin actions
// Generate nonce URL
$delete_url = wp_nonce_url(
admin_url('admin.php?action=delete_post&post_id=123'),
'delete_post_123', // Action (must be unique)
'delete_nonce' // Query parameter name
);
echo '<a href="' . esc_url($delete_url) . '">Delete Post</a>';
// Verify nonce in handler
add_action('admin_action_delete_post', 'handle_delete_post');
function handle_delete_post() {
// Verify nonce from URL
if (!isset($_GET['delete_nonce']) ||
!wp_verify_nonce($_GET['delete_nonce'], 'delete_post_123')) {
wp_die('Invalid security token');
}
// Verify capability
$post_id = absint($_GET['post_id']);
if (!current_user_can('delete_post', $post_id)) {
wp_die('You cannot delete this post');
}
// Delete post
wp_delete_post($post_id, true); // true = force delete
// Redirect with success message
wp_redirect(add_query_arg('message', 'deleted', wp_get_referer()));
exit;
}Pattern 3: AJAX Nonces
Use Case: Frontend AJAX requests
BEFORE (Vulnerable):
// ⚠️ Vulnerable AJAX request
jQuery.post(ajaxurl, {
action: 'update_user_meta',
user_id: 42,
meta_key: 'favorite_color',
meta_value: 'blue'
}, function(response) {
console.log(response);
});AFTER (Secure):
PHP (Enqueue script with nonce):
add_action('wp_enqueue_scripts', 'enqueue_ajax_script');
function enqueue_ajax_script() {
wp_enqueue_script('my-ajax-script',
plugin_dir_url(__FILE__) . 'js/ajax.js',
['jquery'],
'1.0.0',
true
);
// Pass nonce and AJAX URL to JavaScript
wp_localize_script('my-ajax-script', 'myAjax', [
'ajaxurl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('my_ajax_nonce'), // Generate nonce
]);
}
// AJAX handler with nonce verification
add_action('wp_ajax_update_user_meta', 'handle_ajax_update');
function handle_ajax_update() {
// Verify nonce
check_ajax_referer('my_ajax_nonce', 'nonce');
// Verify capability
if (!current_user_can('edit_users')) {
wp_send_json_error(['message' => 'Permission denied']);
}
// Sanitize input
$user_id = absint($_POST['user_id']);
$meta_key = sanitize_key($_POST['meta_key']);
$meta_value = sanitize_text_field($_POST['meta_value']);
// Update meta
update_user_meta($user_id, $meta_key, $meta_value);
wp_send_json_success(['message' => 'Updated successfully']);
}JavaScript (Use nonce in AJAX):
jQuery(document).ready(function($) {
$('#update-button').on('click', function() {
$.post(myAjax.ajaxurl, {
action: 'update_user_meta',
nonce: myAjax.nonce, // Include nonce
user_id: 42,
meta_key: 'favorite_color',
meta_value: 'blue'
}, function(response) {
if (response.success) {
console.log(response.data.message);
} else {
console.error(response.data.message);
}
});
});
});Key Functions:
wp_create_nonce($action)- Generate nonce tokencheck_ajax_referer($action, $query_arg)- Verify AJAX nonce (dies on failure)wp_send_json_success($data)- Send JSON success responsewp_send_json_error($data)- Send JSON error response
Nonce Best Practices
✅ DO:
- Use unique action names (e.g.,
delete_post_$post_id, not justdelete) - Always verify nonces BEFORE processing any data
- Combine nonce checks with capability checks
- Use specific nonce functions (
check_ajax_refererfor AJAX)
❌ DON'T:
- Reuse the same nonce action for multiple operations
More from bobmatnyc
Use this skill in a project
Open any RiverX project, search the Skills panel for bobmatnyc/claude-mpm-skills/wordpress-security-validation, and install it in one click.
