WordPress Security Incident Response Plan: Step-by-Step Guide
Create an effective WordPress security incident response plan with detection, containment, eradication, and recovery procedures.
Having a documented incident response plan ensures you can act quickly and effectively when a security breach occurs. This guide covers the essential steps for handling WordPress security incidents.
Incident Response Phases
A complete response covers five phases:
- Preparation - Before incidents occur
- Detection - Identifying the breach
- Containment - Limiting damage
- Eradication - Removing the threat
- Recovery - Restoring normal operations
Phase 1: Preparation
Prepare before incidents happen:
// Emergency contact list
$emergency_contacts = array(
'incident_lead' => array(
'name' => 'Security Lead',
'phone' => '555-0100',
'email' => 'security@company.com',
),
'hosting_support' => array(
'name' => 'Hosting Provider',
'phone' => '555-0200',
'ticket_url' => 'https://host.com/support',
),
'legal_counsel' => array(
'name' => 'Legal Team',
'email' => 'legal@company.com',
),
);
// Pre-staged response scripts
function enable_maintenance_mode() {
$message = 'We are performing scheduled maintenance. Please check back soon.';
file_put_contents(
ABSPATH . '.maintenance',
'<h1>' . $message . '</h1>'
);
}
function disable_maintenance_mode() {
unlink(ABSPATH . '.maintenance');
}
Phase 2: Detection
Identify security incidents quickly:
function detect_potential_breach() {
$indicators = array();
// Check for new admin users
$recent_admins = get_users(array(
'role' => 'administrator',
'date_query' => array(
array('after' => '-7 days'),
),
));
if (count($recent_admins) > 0) {
$indicators[] = array(
'type' => 'new_admin',
'severity' => 'high',
'details' => 'New administrator accounts created',
'count' => count($recent_admins),
);
}
// Check for modified core files
$core_changes = verify_wordpress_core_integrity();
if (!empty($core_changes)) {
$indicators[] = array(
'type' => 'core_modified',
'severity' => 'critical',
'details' => 'WordPress core files modified',
'files' => $core_changes,
);
}
// Check for suspicious files
$suspicious_files = scan_for_suspicious_files(WP_CONTENT_DIR);
if (!empty($suspicious_files)) {
$indicators[] = array(
'type' => 'suspicious_files',
'severity' => 'high',
'details' => 'Potentially malicious files detected',
'files' => $suspicious_files,
);
}
// Check for unusual database activity
$db_anomalies = check_database_anomalies();
if (!empty($db_anomalies)) {
$indicators[] = array(
'type' => 'db_anomaly',
'severity' => 'medium',
'details' => 'Unusual database patterns detected',
'anomalies' => $db_anomalies,
);
}
return $indicators;
}
Phase 3: Containment
Limit damage and prevent spread:
function contain_incident($severity) {
$actions_taken = array();
// Enable maintenance mode
enable_maintenance_mode();
$actions_taken[] = 'Maintenance mode enabled';
// Change all admin passwords
$admins = get_users(array('role' => 'administrator'));
foreach ($admins as $admin) {
$new_pass = wp_generate_password(24, true);
wp_set_password($new_pass, $admin->ID);
// Store temporarily for notification
set_transient('temp_pass_' . $admin->ID, $new_pass, HOUR_IN_SECONDS);
}
$actions_taken[] = 'Admin passwords reset';
// Invalidate all sessions
foreach ($admins as $admin) {
$sessions = WP_Session_Tokens::get_instance($admin->ID);
$sessions->destroy_all();
}
$actions_taken[] = 'All sessions invalidated';
// Block suspicious IPs
$suspicious_ips = get_suspicious_ips_from_logs();
foreach ($suspicious_ips as $ip) {
block_ip($ip, 'Incident response auto-block');
}
$actions_taken[] = count($suspicious_ips) . ' IPs blocked';
// Disable file editing
if (!defined('DISALLOW_FILE_EDIT')) {
file_put_contents(
ABSPATH . 'wp-config.php',
"\ndefine('DISALLOW_FILE_EDIT', true);",
FILE_APPEND
);
}
$actions_taken[] = 'File editing disabled';
// Log containment actions
log_incident_response('containment', $actions_taken);
return $actions_taken;
}
Phase 4: Eradication
Remove the threat completely:
function eradicate_threat($indicators) {
$actions = array();
foreach ($indicators as $indicator) {
switch ($indicator['type']) {
case 'new_admin':
// Remove unauthorized admin accounts
foreach ($indicator['users'] ?? array() as $user_id) {
if (!is_legitimate_admin($user_id)) {
wp_delete_user($user_id);
$actions[] = 'Removed unauthorized user: ' . $user_id;
}
}
break;
case 'core_modified':
// Restore core files from official source
reinstall_wordpress_core();
$actions[] = 'WordPress core reinstalled';
break;
case 'suspicious_files':
// Quarantine and remove malicious files
foreach ($indicator['files'] as $file) {
$quarantine_path = quarantine_file($file);
unlink($file);
$actions[] = 'Removed: ' . basename($file);
}
break;
case 'db_anomaly':
// Clean database entries
clean_malicious_database_entries($indicator['entries'] ?? array());
$actions[] = 'Database entries cleaned';
break;
}
}
// Regenerate security keys
regenerate_wp_salts();
$actions[] = 'Security keys regenerated';
// Update all plugins and themes
wp_update_plugins();
wp_update_themes();
$actions[] = 'Plugins and themes updated';
log_incident_response('eradication', $actions);
return $actions;
}
function quarantine_file($file_path) {
$quarantine_dir = WP_CONTENT_DIR . '/quarantine/';
if (!file_exists($quarantine_dir)) {
mkdir($quarantine_dir, 0750, true);
file_put_contents($quarantine_dir . '.htaccess', 'deny from all');
}
$new_name = date('Y-m-d-His') . '_' . basename($file_path) . '.quarantine';
$new_path = $quarantine_dir . $new_name;
// Store metadata
file_put_contents($new_path . '.meta', json_encode(array(
'original_path' => $file_path,
'quarantined_at' => current_time('mysql'),
'md5' => md5_file($file_path),
'size' => filesize($file_path),
)));
copy($file_path, $new_path);
return $new_path;
}
Phase 5: Recovery
Restore normal operations safely:
function begin_recovery() {
$checklist = array(
'backups_verified' => false,
'malware_scan_clean' => false,
'passwords_distributed' => false,
'monitoring_enhanced' => false,
'stakeholders_notified' => false,
);
// Verify clean backup exists
$latest_backup = get_latest_clean_backup();
if ($latest_backup && verify_backup_integrity($latest_backup)) {
$checklist['backups_verified'] = true;
}
// Run final malware scan
$scan_results = full_malware_scan();
if (empty($scan_results['threats'])) {
$checklist['malware_scan_clean'] = true;
}
// Distribute new passwords securely
notify_admins_new_passwords();
$checklist['passwords_distributed'] = true;
// Enhance monitoring
enable_enhanced_monitoring();
$checklist['monitoring_enhanced'] = true;
// Notify stakeholders
send_incident_notification();
$checklist['stakeholders_notified'] = true;
// Disable maintenance if all checks pass
if (!in_array(false, $checklist)) {
disable_maintenance_mode();
log_incident_response('recovery_complete', $checklist);
return true;
}
return $checklist;
}
function enable_enhanced_monitoring() {
// Increase logging verbosity
update_option('security_log_level', 'verbose');
// Enable real-time alerts
update_option('security_realtime_alerts', true);
// Schedule more frequent scans
wp_schedule_event(time(), 'hourly', 'enhanced_security_scan');
// Set review date
update_option('enhanced_monitoring_until', date('Y-m-d', strtotime('+30 days')));
}
Post-Incident Review
Learn from the incident:
function generate_incident_report($incident_id) {
$incident = get_incident_data($incident_id);
$report = array(
'summary' => array(
'incident_id' => $incident_id,
'detected_at' => $incident['detected_at'],
'contained_at' => $incident['contained_at'],
'resolved_at' => $incident['resolved_at'],
'total_duration' => calculate_duration($incident),
),
'impact' => array(
'affected_users' => $incident['affected_users'],
'data_exposed' => $incident['data_exposed'],
'downtime' => $incident['downtime_minutes'],
),
'root_cause' => $incident['root_cause'],
'actions_taken' => get_incident_actions($incident_id),
'recommendations' => array(
'immediate' => $incident['immediate_recommendations'],
'long_term' => $incident['long_term_recommendations'],
),
);
return $report;
}
Conclusion
A well-documented incident response plan enables quick, effective action during security breaches. Regular testing and updates ensure your team is prepared when incidents occur. Review and update your plan at least annually.
Written by Sarah Chen
WP Folder Shield Team