Changeset 3356074
- Timestamp:
- 09/04/2025 12:34:07 PM (6 months ago)
- Location:
- axanet-tools/trunk
- Files:
-
- 3 edited
-
README.md (modified) (3 diffs)
-
axanet-tools.php (modified) (2 diffs)
-
includes/login-security.php (modified) (10 diffs)
Legend:
- Unmodified
- Added
- Removed
-
axanet-tools/trunk/README.md
r3355661 r3356074 5 5 Tested up to: 6.8 6 6 Requires PHP: 8.0 7 Stable tag: 1.1. 07 Stable tag: 1.1.1 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 42 42 == Changelog == 43 43 44 = 1.1.1 = 45 * Fixed: Login security improvements 46 44 47 = 1.1.0 = 45 48 * Added: Database Cleanup … … 52 55 = 1.0.0 = 53 56 * Initial release with features for disabling comments, customizing login logo and login security, disabling WP pages, managing admin bar, search & replace and maintenance mode. 54 55 == Upgrade Notice ==56 = 1.0.0 =57 First release of the plugin. -
axanet-tools/trunk/axanet-tools.php
r3355661 r3356074 11 11 Plugin Name: axanet Tools 12 12 Description: Essential tools to edit login logo and login security, disable comments site-wide with optional deletion, disable system pages, manage admin bar visibility, search & replace database strings, clean up database and control maintenance mode. 13 Version: 1.1. 013 Version: 1.1.1 14 14 Author: axanet GmbH 15 15 Author URI: https://axanet.ch … … 24 24 if ( ! defined( 'ABSPATH' ) ) exit; 25 25 26 define( 'AXANET_TOOLS_VERSION', '1.1. 0' );26 define( 'AXANET_TOOLS_VERSION', '1.1.1' ); 27 27 define( 'AXANET_TOOLS_PATH', plugin_dir_path( __FILE__ ) ); 28 28 define( 'AXANET_TOOLS_URL', plugin_dir_url( __FILE__ ) ); -
axanet-tools/trunk/includes/login-security.php
r3354211 r3356074 2 2 /** 3 3 * Login Security for axanet Tools 4 * 5 * Protects against brute-force attempts on wp-login, XML-RPC, and REST API. 4 6 * 5 7 * @package axanet-tools … … 31 33 32 34 /** 33 * Handle failed login 34 */ 35 function axanet_login_security_handle_failed( $username ) { 35 * Get user IP 36 */ 37 function axanet_login_security_get_ip() { 38 if ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) { 39 return sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ); 40 } 41 return false; 42 } 43 44 /** 45 * Increment failed attempts for an IP and block if necessary 46 */ 47 function axanet_login_security_register_failed_attempt( $username = '' ) { 36 48 if ( ! axanet_login_security_is_enabled() ) { 37 49 return; … … 64 76 } 65 77 } 66 add_action( 'wp_login_failed', 'axanet_login_security_handle_failed' ); 67 68 /** 69 * Prevent login if blocked 70 */ 71 function axanet_login_security_block_check( $user ) { 78 79 /** 80 * Check if IP is blocked 81 */ 82 function axanet_login_security_is_ip_blocked() { 72 83 if ( ! axanet_login_security_is_enabled() ) { 73 return $user;84 return false; 74 85 } 75 86 76 87 $ip = axanet_login_security_get_ip(); 77 88 if ( ! $ip ) { 78 return $user;89 return false; 79 90 } 80 91 81 92 $block_key = 'axanet_blocked_' . md5( $ip ); 82 if ( get_transient( $block_key ) ) { 93 return (bool) get_transient( $block_key ); 94 } 95 96 /** 97 * Handle failed login (wp-login) 98 */ 99 function axanet_login_security_wp_login_failed( $username ) { 100 axanet_login_security_register_failed_attempt( $username ); 101 } 102 add_action( 'wp_login_failed', 'axanet_login_security_wp_login_failed' ); 103 104 /** 105 * Prevent login if blocked (wp-login) 106 */ 107 function axanet_login_security_authenticate( $user ) { 108 if ( axanet_login_security_is_ip_blocked() ) { 83 109 return new WP_Error( 84 110 'axanet_blocked', … … 86 112 ); 87 113 } 88 89 114 return $user; 90 115 } 91 add_filter( 'authenticate', 'axanet_login_security_block_check', 30 ); 92 93 /** 94 * Get user IP 95 */ 96 function axanet_login_security_get_ip() { 97 if ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) { 98 return sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ); 99 } 100 return false; 101 } 116 add_filter( 'authenticate', 'axanet_login_security_authenticate', 30 ); 117 118 /** 119 * Handle failed XML-RPC login attempts 120 */ 121 function axanet_login_security_xmlrpc_failed( $error ) { 122 if ( isset( $_SERVER['PHP_AUTH_USER'] ) ) { 123 $username = sanitize_text_field( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) ); 124 axanet_login_security_register_failed_attempt( $username ); 125 } 126 return $error; 127 } 128 add_filter( 'xmlrpc_login_error', 'axanet_login_security_xmlrpc_failed' ); 129 130 /** 131 * Block XML-RPC logins from blocked IPs 132 */ 133 function axanet_login_security_block_xmlrpc() { 134 if ( axanet_login_security_is_ip_blocked() ) { 135 wp_die( 136 __( 'Too many failed login attempts. Please try again later.', 'axanet-tools' ), 137 __( 'Blocked', 'axanet-tools' ), 138 [ 'response' => 403 ] 139 ); 140 } 141 } 142 add_action( 'xmlrpc_call', 'axanet_login_security_block_xmlrpc', 0 ); 143 144 /** 145 * Block REST API logins from blocked IPs 146 */ 147 function axanet_login_security_block_rest( $user, $username, $password ) { 148 if ( axanet_login_security_is_ip_blocked() ) { 149 return new WP_Error( 150 'axanet_blocked', 151 __( 'Too many failed login attempts. Please try again later.', 'axanet-tools' ), 152 [ 'status' => 403 ] 153 ); 154 } 155 return $user; 156 } 157 add_filter( 'rest_authentication_errors', function( $result ) { 158 if ( ! empty( $result ) ) { 159 return $result; // skip if already blocked 160 } 161 162 if ( axanet_login_security_is_ip_blocked() ) { 163 return new WP_Error( 164 'axanet_blocked', 165 __( 'Too many failed login attempts. Please try again later.', 'axanet-tools' ), 166 [ 'status' => 403 ] 167 ); 168 } 169 170 return $result; 171 }); 172 173 /** 174 * Handle failed REST API login attempts 175 */ 176 add_filter( 'determine_current_user', function( $user_id ) { 177 if ( ! axanet_login_security_is_enabled() ) { 178 return $user_id; 179 } 180 181 // If REST login attempt failed, register it 182 if ( defined( 'REST_REQUEST' ) && REST_REQUEST && empty( $user_id ) ) { 183 $username = sanitize_text_field( wp_unslash( $_REQUEST['username'] ?? '' ) ); 184 if ( $username ) { 185 axanet_login_security_register_failed_attempt( $username ); 186 } 187 } 188 189 return $user_id; 190 }, 100 ); 102 191 103 192 /** … … 109 198 } 110 199 111 $error_message = '';112 200 $is_enabled = axanet_login_security_is_enabled(); 113 201 … … 117 205 118 206 if ( $action === 'enable' ) { 119 if ( update_option( 'axanet_login_security_enabled', 1 ) ) { 120 $is_enabled = true; 121 echo '<div class="notice notice-success"><p>' . esc_html__( 'Login Security enabled.', 'axanet-tools' ) . '</p></div>'; 122 } 207 update_option( 'axanet_login_security_enabled', 1 ); 208 $is_enabled = true; 209 echo '<div class="notice notice-success"><p>' . esc_html__( 'Login Security enabled.', 'axanet-tools' ) . '</p></div>'; 123 210 } elseif ( $action === 'disable' ) { 124 if ( update_option( 'axanet_login_security_enabled', 0 ) ) { 125 $is_enabled = false; 126 echo '<div class="notice notice-success"><p>' . esc_html__( 'Login Security disabled.', 'axanet-tools' ) . '</p></div>'; 127 } 211 update_option( 'axanet_login_security_enabled', 0 ); 212 $is_enabled = false; 213 echo '<div class="notice notice-success"><p>' . esc_html__( 'Login Security disabled.', 'axanet-tools' ) . '</p></div>'; 128 214 } 129 215 } … … 140 226 } 141 227 142 // Handle unlock228 // Handle IP unlock 143 229 if ( isset( $_POST['axanet_unlock_ip'] ) && check_admin_referer( 'axanet_login_security_unlock' ) ) { 144 230 $ip = isset( $_POST['ip'] ) ? sanitize_text_field( wp_unslash( $_POST['ip'] ) ) : ''; … … 149 235 update_option( 'axanet_blocked_ips', $blocked ); 150 236 delete_transient( 'axanet_blocked_' . md5( $ip ) ); 151 152 /* translators: %s: The IP address that was unblocked. */153 237 echo '<div class="notice notice-success"><p>' . esc_html( sprintf( esc_html__( 'IP %s has been unblocked.', 'axanet-tools' ), $ip ) ) . '</p></div>'; 154 238 } … … 156 240 } 157 241 158 159 242 $settings = axanet_login_security_get_settings(); 160 243 $blocked = get_option( 'axanet_blocked_ips', [] ); 244 161 245 ?> 162 246 <div class="wrap"> … … 174 258 </p> 175 259 <?php if ( $is_enabled ) : ?> 176 <button type="submit" name="login_security_action" value="disable" class="axanet-button axanet-disable" aria-label="<?php esc_attr_e( 'Disable Login Security', 'axanet-tools' ); ?>" data-confirm="<?php esc_attr_e( 'Are you sure you want to disable login security?', 'axanet-tools' ); ?>">260 <button type="submit" name="login_security_action" value="disable" class="axanet-button axanet-disable" aria-label="<?php esc_attr_e( 'Disable Login Security', 'axanet-tools' ); ?>"> 177 261 <?php esc_html_e( 'Disable', 'axanet-tools' ); ?> 178 262 </button>
Note: See TracChangeset
for help on using the changeset viewer.