Security Overview
Security considerations and best practices for WPZylos plugins.
Security Principles
- Never trust user input — Validate and sanitize all input
- Always escape output — Escape all data before display
- Use prepared statements — Prevent SQL injection
- Verify capabilities — Check user permissions
- Verify intent — Use nonces for all state-changing actions
Input Security
Sanitization
Clean input before use:
use WPZylos\Framework\Security\Sanitizer;
$sanitizer = new Sanitizer();
$title = $sanitizer->text($_POST['title']); // Plain text
$content = $sanitizer->html($_POST['content']); // Safe HTML
$email = $sanitizer->email($_POST['email']); // Email
$url = $sanitizer->url($_POST['url']); // URL
$number = $sanitizer->int($_POST['count']); // Integer
Validation
Verify input meets requirements:
use WPZylos\Framework\Validation\Validator;
$validator = new Validator();
$result = $validator->validate($_POST, [
'email' => 'required|email',
'age' => 'required|numeric|min:18',
'role' => 'required|in:admin,editor,user',
]);
if ($result->fails()) {
// Handle validation errors
}
Output Security
Always escape output. Use the appropriate function:
| Function | Use For |
|---|---|
esc_html() | Text content |
esc_attr() | HTML attributes |
esc_url() | URLs |
esc_js() | JavaScript strings |
esc_textarea() | Textarea content |
wp_kses_post() | Post content (safe HTML) |
wp_kses() | Custom allowed HTML |
<h1><?php echo esc_html($title); ?></h1>
<input type="text" value="<?php echo esc_attr($value); ?>">
<a href="<?php echo esc_url($link); ?>">Link</a>
Authentication & Authorization
Capability Checks
use WPZylos\Framework\Security\Gate;
$gate = new Gate();
// Check capability
if (!$gate->can('manage_options')) {
wp_die('Unauthorized');
}
// Check with object
if (!$gate->can('edit_post', $postId)) {
wp_die('Cannot edit this post');
}
Nonce Verification
use WPZylos\Framework\Security\Nonce;
$nonce = new Nonce($context);
// Create nonce (in form)
$token = $nonce->create('save_settings');
// Verify nonce (on submit)
if (!$nonce->verify($_POST['_nonce'], 'save_settings')) {
wp_die('Security check failed');
}
Database Security
Prepared Statements
Always use prepared statements:
// Bad: Vulnerable to SQL injection
$wpdb->query("SELECT * FROM users WHERE id = " . $_GET['id']);
// Good: Safe with preparation
$wpdb->get_row($wpdb->prepare(
"SELECT * FROM users WHERE id = %d",
(int) $_GET['id']
));
// Good: Safe with QueryBuilder
$builder->where('id', $_GET['id'])->first();
Safe Identifiers
Table and column names cannot be prepared. Whitelist them:
$allowed = ['name', 'email', 'created_at'];
$column = in_array($_GET['sort'], $allowed) ? $_GET['sort'] : 'created_at';
$builder->orderBy($column);
File Upload Security
use WPZylos\Framework\Security\UploadSecurity;
$uploader = new UploadSecurity($context, $nonce, $gate);
// Restrict file types
$uploader->allowMimes([
'jpg|jpeg' => 'image/jpeg',
'png' => 'image/png',
'pdf' => 'application/pdf',
]);
// Restrict file size
$uploader->maxSize(5 * 1024 * 1024); // 5MB
$result = $uploader->handle($_FILES['upload'], 'upload_action');
if (is_wp_error($result)) {
wp_die($result->get_error_message());
}
AJAX Security
Always verify nonces and capabilities in AJAX handlers:
add_action('wp_ajax_mp_save', function () use ($nonce, $gate, $sanitizer) {
// 1. Verify nonce
if (!$nonce->verify($_POST['_nonce'] ?? '', 'save')) {
wp_send_json_error('Invalid nonce', 403);
}
// 2. Check capability
if (!$gate->can('manage_options')) {
wp_send_json_error('Unauthorized', 403);
}
// 3. Sanitize input
$data = $sanitizer->text($_POST['data'] ?? '');
// 4. Process request...
wp_send_json_success();
});
Rate Limiting
Prevent brute force and abuse:
use WPZylos\Framework\Security\RateLimiter;
$limiter = new RateLimiter($context, 5, 60); // 5 attempts per 60 seconds
$key = 'login_' . $_SERVER['REMOTE_ADDR'];
if ($limiter->tooManyAttempts($key)) {
$wait = $limiter->availableIn($key);
wp_die("Too many attempts. Try again in {$wait} seconds.");
}
$limiter->hit($key);
Security Checklist
- All user input is sanitized
- All output is escaped
- All database queries use prepared statements
- Nonces protect all state-changing actions
- Capability checks protect sensitive operations
- File uploads restrict allowed types and sizes
- AJAX handlers verify nonces and capabilities
- Rate limiting protects sensitive endpoints
- No sensitive data in error messages
- Direct file access is prevented