Tutorials

WordPress Security Incident Response Plan: Step-by-Step Guide

Create an effective WordPress security incident response plan with detection, containment, eradication, and recovery procedures.

S
Sarah Chen
10 min read
2,592 views
WordPress security incident response planning guide

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.

Share:
S
Written by Sarah Chen

WP Folder Shield Team

Related Articles

The Ultimate Guide to WordPress Security in 2026
The Ultimate Guide to WordPress Security in 2026

Learn how to protect your WordPress website from hackers, malware, and security threats with this...

January 15, 2026
How to Scan Your WordPress Site for SEO Spam and Hidden Malicious Content
How to Scan Your WordPress Site for SEO Spam and Hidden Malicious Content

Learn effective methods to scan your WordPress site for hidden SEO spam, malicious links, and...

January 13, 2026
How to Protect Your WordPress Uploads Folder from Malware
How to Protect Your WordPress Uploads Folder from Malware

The wp-content/uploads folder is one of the most vulnerable directories in WordPress. Learn how to...

January 13, 2026

Ready to Secure Your WordPress Site?

Get complete protection with WP Folder Shield.

Get Started