Plugin Directory

Changeset 3376009


Ignore:
Timestamp:
10/09/2025 11:43:06 PM (4 months ago)
Author:
pcescato
Message:

Update to version 2.0.0 - Major release, with an admin page for options

Location:
stop-xml-rpc-attacks/trunk
Files:
2 added
2 edited

Legend:

Unmodified
Added
Removed
  • stop-xml-rpc-attacks/trunk/readme.txt

    r3275121 r3376009  
    1 === stop XML-RPC Attacks ===
     1=== Stop XML-RPC Attacks ===
    22Contributors: pcescato
    3 Tags: xml-rpc, pingback, ddos, multicall
    4 Requires at least: 5.0
     3Tags: security, xmlrpc, brute force, ddos, jetpack
     4Requires at least: 6.0
    55Tested up to: 6.8
    66Requires PHP: 7.4
    7 Stable tag: 1.0.1
     7Stable tag: 2.0.0
    88License: GPLv3
    9 License URI: https://www.gnu.org/licenses/gpl.html
     9License URI: https://www.gnu.org/licenses/gpl-3.0.html
    1010
    11 Secure your site's XML-RPC by removing some methods, while you can still use XML-RPC.
     11Blocks dangerous XML-RPC methods while preserving Jetpack, WooCommerce, and mobile apps compatibility.
    1212
    1313== Description ==
    1414
    15 Secure your site's XML-RPC by removing some methods, instead of disabling totally XML-RPC, which is needed by some plugins (eg. Jetpack) and some mobile apps.
     15Stop XML-RPC Attacks protects your WordPress site from XML-RPC brute force attacks, DDoS attempts, and reconnaissance probes while maintaining compatibility with essential services like Jetpack and WooCommerce.
    1616
    17 = Features =
     17**Features:**
    1818
    19 Removes the following methods from XML-RPC interface.
     19* Three security modes: Full Disable, Guest Disable, or Selective Blocking
     20* Blocks dangerous methods: system.multicall, pingback.ping, and more
     21* Compatible with Jetpack and WooCommerce
     22* Optional user enumeration blocking
     23* Attack logging for monitoring
     24* Zero configuration required - works out of the box
     25* Clean, intuitive admin interface
    2026
    21 * system.multicall
    22 * system.listMethods
    23 * system.getCapabilities
    24 * pingback.extensions.getPingbacks
    25 * pingback.ping
    26 * X-Pingback from HTTP headers
    27 
    28 This is not perfect, but it will help prerventing attacks
    29 
    30 = Requirements =
    31 
    32 * WordPress 5.0 or higher.
    33        
    3427== Installation ==
    3528
    36 * Extract the zip file and just drop the contents in the <code>wp-content/plugins/</code> directory of your WordPress installation or install it directly from your dashboard and then activate the plugin from Plugins page.
    37 * There's not options page, simply install and activate.
     291. Upload the plugin files to `/wp-content/plugins/stop-xmlrpc-attacks/`
     302. Activate the plugin through the 'Plugins' menu in WordPress
     313. Go to Settings > XML-RPC Security to configure (optional)
    3832
    3933== Frequently Asked Questions ==
    4034
    41 = Is there something to do after install? =
     35= Will this break Jetpack? =
    4236
    43 Yes, just activate it!
     37No! The default "Selective Blocking" mode is fully compatible with Jetpack and WooCommerce.
    4438
    45 = I already have a security plugin, do I need this plugin too? =
     39= What's the difference between the security modes? =
    4640
    47 It depends on your security plugin. Some secure XML-RPC, some just allow you to enable or disable it, some can stop attacks as *Stop XML-RPC Attacks* does. So you may have to read your security plugin FAQ / doc.
     41* **Full Disable**: Maximum security, disables XML-RPC completely
     42* **Guest Disable**: Balanced approach, only allows XML-RPC for logged-in users
     43* **Selective Blocking**: Best compatibility, only blocks dangerous methods
    4844
    49  
     45= How do I enable logging? =
     46
     47Go to Settings > XML-RPC Security and check "Enable Attack Logging". Logs will be written to your debug.log file when WP_DEBUG is enabled.
     48
    5049== Changelog ==
    5150
    52 = 1.0 =
     51= 2.0.0 =
     52* Added admin interface with visual settings
     53* Three security modes to choose from
     54* Optional attack logging
     55* Improved code quality and security
     56* Full internationalization support
    5357
     58= 1.0.1 =
    5459* Initial release
     60* Basic blocking of dangerous methods
     61
     62== Upgrade Notice ==
     63
     64= 2.0.0 =
     65Major update with admin interface.
  • stop-xml-rpc-attacks/trunk/stop-xml-rpc-attacks.php

    r2812646 r3376009  
    11<?php
    2 
    32/*
    43  Plugin Name: Stop XML-RPC Attacks
    5   Description: Secure your site's XML-RPC by removing some methods, while you can still use XML-RPC.
     4  Description: Blocks dangerous XML-RPC methods while preserving Jetpack, WooCommerce, and mobile apps. Choose between full disable, guest-only disable, or selective blocking.
    65  Author: Pascal CESCATO
    7   Version: 1.0.1
    8   Author URI: https://tsw.ovh
     6  Version: 2.0.0
     7  Author URI: https://zone-test.ovh
    98  License: GPLv3
    10  */
    11 
    12 /*
    13   This program is free software: you can redistribute it and/or modify
    14   it under the terms of the GNU General Public License version 2 as published by
    15   the Free Software Foundation.
    16 
    17   This program is distributed in the hope that it will be useful,
    18   but WITHOUT ANY WARRANTY; without even the implied warranty of
    19   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    20   GNU General Public License for more details.
    21 
    22   You should have received a copy of the GNU General Public License
    23   along with this program.  If not, see <https://www.gnu.org/licenses/gpl.html>.
    24  */
    25 
    26 add_filter('xmlrpc_methods', function ($methods) {
    27     unset($methods['system.multicall']);
    28     unset($methods['system.listMethods']);
    29     unset($methods['system.getCapabilities']);
    30     unset($methods['pingback.extensions.getPingbacks']);
    31     unset($methods['pingback.ping']);
    32     return $methods;
    33 });
    34 
    35 add_action('wp', function () {
    36     header_remove('X-Pingback');
    37 }, 9999);
     9  Text Domain: stop-xml-rpc-attacks
     10  Domain Path: /languages
     11*/
     12
     13if (!defined('ABSPATH')) exit;
     14
     15define('SXRA_VERSION', '2.0.0');
     16
     17class Stop_XMLRPC_Attacks {
     18    private static $instance = null;
     19    private $option_name = 'sxra_settings';
     20    private $default_blocked_methods = [
     21        'system.multicall',
     22        'system.listMethods',
     23        'system.getCapabilities',
     24        'pingback.ping',
     25        'pingback.extensions.getPingbacks',
     26    ];
     27   
     28    private $enumeration_methods = [
     29        'wp.getUsersBlogs',
     30        'wp.getCategories',
     31        'wp.getTags',
     32    ];
     33
     34    public static function init() {
     35        if (self::$instance === null) {
     36            self::$instance = new self();
     37        }
     38        return self::$instance;
     39    }
     40
     41    private function __construct() {
     42        // Admin interface
     43        if (is_admin()) {
     44            add_action('admin_menu', [$this, 'add_admin_menu']);
     45            add_action('admin_init', [$this, 'register_settings']);
     46            add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_styles']);
     47        }
     48
     49        // Get settings
     50        $settings = $this->get_settings();
     51        $mode = $settings['security_mode'];
     52
     53        // Apply security mode
     54        switch ($mode) {
     55            case 'full_disable':
     56                add_filter('xmlrpc_enabled', '__return_false');
     57                break;
     58
     59            case 'guest_disable':
     60                add_filter('xmlrpc_enabled', [$this, 'disable_for_guests']);
     61                add_filter('xmlrpc_methods', [$this, 'block_dangerous_methods']);
     62                break;
     63
     64            case 'selective':
     65            default:
     66                add_filter('xmlrpc_methods', [$this, 'block_dangerous_methods']);
     67                break;
     68        }
     69
     70        // Always remove headers and links
     71        add_action('init', [$this, 'remove_headers_and_links'], 1);
     72        add_action('send_headers', [$this, 'remove_pingback_header']);
     73
     74        // Optional logging
     75        if ($settings['enable_logging']) {
     76            add_filter('xmlrpc_call', [$this, 'log_blocked_attempts']);
     77        }
     78    }
     79
     80    private function get_settings() {
     81        $defaults = [
     82            'security_mode' => 'selective',
     83            'block_enumeration' => false,
     84            'enable_logging' => false,
     85        ];
     86        return wp_parse_args(get_option($this->option_name, []), $defaults);
     87    }
     88
     89    public function add_admin_menu() {
     90        add_options_page(
     91            __('Stop XML-RPC Attacks', 'stop-xml-rpc-attacks'),
     92            __('XML-RPC Security', 'stop-xml-rpc-attacks'),
     93            'manage_options',
     94            'stop-xmlrpc-attacks',
     95            [$this, 'render_admin_page']
     96        );
     97    }
     98
     99    public function register_settings() {
     100        register_setting($this->option_name, $this->option_name, [$this, 'sanitize_settings']);
     101    }
     102
     103    public function sanitize_settings($input) {
     104        $sanitized = [];
     105        $sanitized['security_mode'] = in_array($input['security_mode'], ['full_disable', 'guest_disable', 'selective'])
     106            ? $input['security_mode']
     107            : 'selective';
     108        $sanitized['block_enumeration'] = isset($input['block_enumeration']);
     109        $sanitized['enable_logging'] = isset($input['enable_logging']);
     110        return $sanitized;
     111    }
     112
     113    public function enqueue_admin_styles($hook) {
     114        if ($hook !== 'settings_page_stop-xmlrpc-attacks') {
     115            return;
     116        }
     117       
     118        wp_add_inline_style('wp-admin', '
     119            .sxra-card {
     120                background: #fff;
     121                border: 1px solid #ccd0d4;
     122                border-radius: 4px;
     123                padding: 20px;
     124                margin-bottom: 20px;
     125                box-shadow: 0 1px 1px rgba(0,0,0,.04);
     126            }
     127            .sxra-card h3 {
     128                margin-top: 0;
     129                border-bottom: 1px solid #eee;
     130                padding-bottom: 10px;
     131            }
     132            .sxra-option {
     133                margin: 15px 0;
     134                padding: 15px;
     135                background: #f9f9f9;
     136                border-left: 4px solid #2271b1;
     137                border-radius: 3px;
     138            }
     139            .sxra-option label {
     140                font-weight: 600;
     141                display: block;
     142                margin-bottom: 5px;
     143            }
     144            .sxra-option p {
     145                margin: 5px 0 0 0;
     146                color: #646970;
     147                font-size: 13px;
     148            }
     149            .sxra-stats {
     150                display: grid;
     151                grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
     152                gap: 15px;
     153                margin-top: 20px;
     154            }
     155            .sxra-stat-box {
     156                background: #f0f6fc;
     157                border-left: 4px solid #2271b1;
     158                padding: 15px;
     159                border-radius: 3px;
     160            }
     161            .sxra-stat-box strong {
     162                display: block;
     163                font-size: 24px;
     164                color: #2271b1;
     165                margin-bottom: 5px;
     166            }
     167            .sxra-warning {
     168                background: #fcf3cd;
     169                border-left-color: #dba617;
     170            }
     171            .sxra-success {
     172                background: #d5f4e6;
     173                border-left-color: #00a32a;
     174            }
     175        ');
     176    }
     177
     178    private function get_security_mode_label($mode) {
     179        $labels = [
     180            'full_disable' => __('Full Disable', 'stop-xml-rpc-attacks'),
     181            'guest_disable' => __('Guest Disable', 'stop-xml-rpc-attacks'),
     182            'selective' => __('Selective Blocking', 'stop-xml-rpc-attacks'),
     183        ];
     184        return isset($labels[$mode]) ? $labels[$mode] : $labels['selective'];
     185    }
     186
     187    public function render_admin_page() {
     188        if (!current_user_can('manage_options')) {
     189            return;
     190        }
     191
     192        $settings = $this->get_settings();
     193        $blocked_count = count($this->default_blocked_methods);
     194        if ($settings['block_enumeration']) {
     195            $blocked_count += count($this->enumeration_methods);
     196        }
     197
     198        ?>
     199        <div class="wrap">
     200            <h1><?php echo esc_html(get_admin_page_title()); ?></h1>
     201            <p><?php esc_html_e('Protect your WordPress site from XML-RPC attacks while maintaining compatibility with essential services.', 'stop-xml-rpc-attacks'); ?></p>
     202
     203            <div class="sxra-card">
     204                <h3><?php esc_html_e('Current Status', 'stop-xml-rpc-attacks'); ?></h3>
     205                <div class="sxra-stats">
     206                    <div class="sxra-stat-box sxra-success">
     207                        <strong><?php echo esc_html($blocked_count); ?></strong>
     208                        <span><?php esc_html_e('Methods Blocked', 'stop-xml-rpc-attacks'); ?></span>
     209                    </div>
     210                    <div class="sxra-stat-box">
     211                        <strong><?php echo esc_html($this->get_security_mode_label($settings['security_mode'])); ?></strong>
     212                        <span><?php esc_html_e('Security Mode', 'stop-xml-rpc-attacks'); ?></span>
     213                    </div>
     214                    <div class="sxra-stat-box <?php echo $settings['enable_logging'] ? 'sxra-success' : ''; ?>">
     215                        <strong><?php echo $settings['enable_logging'] ? esc_html__('ON', 'stop-xml-rpc-attacks') : esc_html__('OFF', 'stop-xml-rpc-attacks'); ?></strong>
     216                        <span><?php esc_html_e('Attack Logging', 'stop-xml-rpc-attacks'); ?></span>
     217                    </div>
     218                </div>
     219            </div>
     220
     221            <form method="post" action="options.php">
     222                <?php settings_fields($this->option_name); ?>
     223
     224                <div class="sxra-card">
     225                    <h3><?php esc_html_e('Security Mode', 'stop-xml-rpc-attacks'); ?></h3>
     226                   
     227                    <div class="sxra-option <?php echo $settings['security_mode'] === 'full_disable' ? 'sxra-success' : ''; ?>">
     228                        <label>
     229                            <input type="radio" name="<?php echo esc_attr($this->option_name); ?>[security_mode]"
     230                                   value="full_disable" <?php checked($settings['security_mode'], 'full_disable'); ?>>
     231                            <strong><?php esc_html_e('Full Disable', 'stop-xml-rpc-attacks'); ?></strong> <?php esc_html_e('(Maximum Security)', 'stop-xml-rpc-attacks'); ?>
     232                        </label>
     233                        <p><?php esc_html_e('Completely disables XML-RPC. Use this if you don\'t need Jetpack, WooCommerce mobile apps, or WordPress mobile app.', 'stop-xml-rpc-attacks'); ?></p>
     234                    </div>
     235
     236                    <div class="sxra-option <?php echo $settings['security_mode'] === 'guest_disable' ? 'sxra-warning' : ''; ?>">
     237                        <label>
     238                            <input type="radio" name="<?php echo esc_attr($this->option_name); ?>[security_mode]"
     239                                   value="guest_disable" <?php checked($settings['security_mode'], 'guest_disable'); ?>>
     240                            <strong><?php esc_html_e('Guest Disable', 'stop-xml-rpc-attacks'); ?></strong> <?php esc_html_e('(Balanced)', 'stop-xml-rpc-attacks'); ?>
     241                        </label>
     242                        <p><?php esc_html_e('Disables XML-RPC for non-logged-in users, but allows it for administrators. Blocks dangerous methods for logged-in users.', 'stop-xml-rpc-attacks'); ?></p>
     243                    </div>
     244
     245                    <div class="sxra-option <?php echo $settings['security_mode'] === 'selective' ? 'sxra-warning' : ''; ?>">
     246                        <label>
     247                            <input type="radio" name="<?php echo esc_attr($this->option_name); ?>[security_mode]"
     248                                   value="selective" <?php checked($settings['security_mode'], 'selective'); ?>>
     249                            <strong><?php esc_html_e('Selective Blocking', 'stop-xml-rpc-attacks'); ?></strong> <?php esc_html_e('(Best Compatibility)', 'stop-xml-rpc-attacks'); ?>
     250                        </label>
     251                        <p><?php esc_html_e('Only blocks known dangerous methods. Use this if you need full Jetpack or WooCommerce functionality.', 'stop-xml-rpc-attacks'); ?></p>
     252                    </div>
     253                </div>
     254
     255                <div class="sxra-card">
     256                    <h3><?php esc_html_e('Advanced Options', 'stop-xml-rpc-attacks'); ?></h3>
     257                   
     258                    <div class="sxra-option">
     259                        <label>
     260                            <input type="checkbox" name="<?php echo esc_attr($this->option_name); ?>[block_enumeration]"
     261                                   value="1" <?php checked($settings['block_enumeration']); ?>>
     262                            <strong><?php esc_html_e('Block User Enumeration Methods', 'stop-xml-rpc-attacks'); ?></strong>
     263                        </label>
     264                        <p><?php esc_html_e('Blocks methods that can reveal usernames. Warning: This will break WordPress mobile apps and some third-party tools.', 'stop-xml-rpc-attacks'); ?></p>
     265                    </div>
     266
     267                    <div class="sxra-option">
     268                        <label>
     269                            <input type="checkbox" name="<?php echo esc_attr($this->option_name); ?>[enable_logging]"
     270                                   value="1" <?php checked($settings['enable_logging']); ?>>
     271                            <strong><?php esc_html_e('Enable Attack Logging', 'stop-xml-rpc-attacks'); ?></strong>
     272                        </label>
     273                        <p><?php esc_html_e('Logs blocked XML-RPC attempts to your debug.log file. Useful for monitoring attacks.', 'stop-xml-rpc-attacks'); ?></p>
     274                    </div>
     275                </div>
     276
     277                <div class="sxra-card">
     278                    <h3><?php esc_html_e('Blocked Methods', 'stop-xml-rpc-attacks'); ?></h3>
     279                    <p><strong>
     280                        <?php
     281                        /* translators: %d: number of blocked methods */
     282                        echo esc_html(sprintf(__('Always blocked (%d):', 'stop-xml-rpc-attacks'), count($this->default_blocked_methods)));
     283                        ?>
     284                    </strong></p>
     285                    <ul>
     286                        <?php foreach ($this->default_blocked_methods as $method): ?>
     287                            <li><code><?php echo esc_html($method); ?></code></li>
     288                        <?php endforeach; ?>
     289                    </ul>
     290
     291                    <?php if ($settings['block_enumeration']): ?>
     292                        <p><strong>
     293                            <?php
     294                            /* translators: %d: number of additionally blocked methods */
     295                            echo esc_html(sprintf(__('Additionally blocked (%d):', 'stop-xml-rpc-attacks'), count($this->enumeration_methods)));
     296                            ?>
     297                        </strong></p>
     298                        <ul>
     299                            <?php foreach ($this->enumeration_methods as $method): ?>
     300                                <li><code><?php echo esc_html($method); ?></code></li>
     301                            <?php endforeach; ?>
     302                        </ul>
     303                    <?php endif; ?>
     304                </div>
     305
     306                <?php submit_button(__('Save Security Settings', 'stop-xml-rpc-attacks'), 'primary large'); ?>
     307            </form>
     308
     309            <div class="sxra-card">
     310                <h3><?php esc_html_e('About This Plugin', 'stop-xml-rpc-attacks'); ?></h3>
     311                <p><strong><?php esc_html_e('Version:', 'stop-xml-rpc-attacks'); ?></strong> <?php echo esc_html(SXRA_VERSION); ?></p>
     312                <p><strong><?php esc_html_e('Author:', 'stop-xml-rpc-attacks'); ?></strong> <a href="https://tsw.ovh" target="_blank" rel="noopener noreferrer">Pascal CESCATO</a></p>
     313                <p><?php esc_html_e('This plugin protects your WordPress site from XML-RPC brute force attacks, DDoS attempts, and reconnaissance probes while maintaining compatibility with essential services like Jetpack and WooCommerce.', 'stop-xml-rpc-attacks'); ?></p>
     314            </div>
     315        </div>
     316        <?php
     317    }
     318
     319    public function disable_for_guests($enabled) {
     320        return is_user_logged_in() ? $enabled : false;
     321    }
     322
     323    public function block_dangerous_methods($methods) {
     324        if (!defined('XMLRPC_REQUEST') || !XMLRPC_REQUEST) {
     325            return $methods;
     326        }
     327
     328        $settings = $this->get_settings();
     329        $blocked = $this->default_blocked_methods;
     330
     331        if ($settings['block_enumeration']) {
     332            $blocked = array_merge($blocked, $this->enumeration_methods);
     333        }
     334
     335        $blocked = apply_filters('sxra_blocked_xmlrpc_methods', $blocked);
     336
     337        foreach ($blocked as $method) {
     338            unset($methods[$method]);
     339        }
     340
     341        return $methods;
     342    }
     343
     344    public function log_blocked_attempts($method) {
     345        $settings = $this->get_settings();
     346        $blocked = $this->default_blocked_methods;
     347       
     348        if ($settings['block_enumeration']) {
     349            $blocked = array_merge($blocked, $this->enumeration_methods);
     350        }
     351       
     352        if (in_array($method, $blocked)) {
     353            $ip = isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : ('CLI' === php_sapi_name() ? 'CLI' : 'unknown');
     354           
     355            // Only log in development/debug mode
     356            if (defined('WP_DEBUG') && WP_DEBUG) {
     357                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Only runs when WP_DEBUG is enabled
     358                error_log(sprintf(
     359                    '[Stop XML-RPC v%s] Blocked dangerous method "%s" from IP: %s',
     360                    SXRA_VERSION,
     361                    $method,
     362                    $ip
     363                ));
     364            }
     365        }
     366        return $method;
     367    }
     368
     369    public function remove_headers_and_links() {
     370        remove_action('wp_head', 'rsd_link');
     371        remove_action('wp_head', 'wlwmanifest_link');
     372       
     373        add_filter('wp_headers', function($headers) {
     374            unset($headers['X-Pingback']);
     375            return $headers;
     376        });
     377    }
     378
     379    public function remove_pingback_header() {
     380        if (function_exists('header_remove')) {
     381            header_remove('X-Pingback');
     382        }
     383    }
     384}
     385
     386// Initialize plugin
     387Stop_XMLRPC_Attacks::init();
Note: See TracChangeset for help on using the changeset viewer.