Changeset 3435758
- Timestamp:
- 01/09/2026 09:30:04 AM (3 months ago)
- Location:
- nhrrob-secure
- Files:
-
- 62 added
- 6 edited
- 1 copied
-
assets/screenshot-3.png (modified) (previous)
-
assets/screenshot-4.png (modified) (previous)
-
assets/screenshot-5.png (added)
-
assets/screenshot-6.png (added)
-
tags/1.0.4 (copied) (copied from nhrrob-secure/trunk)
-
tags/1.0.4/assets (added)
-
tags/1.0.4/assets/src (added)
-
tags/1.0.4/assets/src/components (added)
-
tags/1.0.4/assets/src/components/CustomLoginPage.js (added)
-
tags/1.0.4/assets/src/components/FileProtection.js (added)
-
tags/1.0.4/assets/src/components/LoginProtection.js (added)
-
tags/1.0.4/assets/src/components/TwoFactorAuth.js (added)
-
tags/1.0.4/assets/src/index.js (added)
-
tags/1.0.4/assets/src/profile.css (added)
-
tags/1.0.4/assets/src/style.css (added)
-
tags/1.0.4/build (added)
-
tags/1.0.4/build/admin.asset.php (added)
-
tags/1.0.4/build/admin.css (added)
-
tags/1.0.4/build/admin.js (added)
-
tags/1.0.4/build/profile.asset.php (added)
-
tags/1.0.4/build/profile.css (added)
-
tags/1.0.4/build/profile.js (added)
-
tags/1.0.4/includes (added)
-
tags/1.0.4/includes/Admin (added)
-
tags/1.0.4/includes/Admin.php (added)
-
tags/1.0.4/includes/Admin/Api.php (added)
-
tags/1.0.4/includes/Admin/Menu.php (added)
-
tags/1.0.4/includes/App.php (added)
-
tags/1.0.4/includes/Assets.php (added)
-
tags/1.0.4/includes/Security.php (added)
-
tags/1.0.4/includes/TwoFactor.php (added)
-
tags/1.0.4/nhrrob-secure.php (modified) (2 diffs)
-
tags/1.0.4/readme.txt (modified) (4 diffs)
-
tags/1.0.4/tailwind.config.js (added)
-
tags/1.0.4/templates (added)
-
tags/1.0.4/templates/login-2fa-form.php (added)
-
tags/1.0.4/templates/profile-2fa.php (added)
-
trunk/assets (added)
-
trunk/assets/src (added)
-
trunk/assets/src/components (added)
-
trunk/assets/src/components/CustomLoginPage.js (added)
-
trunk/assets/src/components/FileProtection.js (added)
-
trunk/assets/src/components/LoginProtection.js (added)
-
trunk/assets/src/components/TwoFactorAuth.js (added)
-
trunk/assets/src/index.js (added)
-
trunk/assets/src/profile.css (added)
-
trunk/assets/src/style.css (added)
-
trunk/build (added)
-
trunk/build/admin.asset.php (added)
-
trunk/build/admin.css (added)
-
trunk/build/admin.js (added)
-
trunk/build/profile.asset.php (added)
-
trunk/build/profile.css (added)
-
trunk/build/profile.js (added)
-
trunk/includes (added)
-
trunk/includes/Admin (added)
-
trunk/includes/Admin.php (added)
-
trunk/includes/Admin/Api.php (added)
-
trunk/includes/Admin/Menu.php (added)
-
trunk/includes/App.php (added)
-
trunk/includes/Assets.php (added)
-
trunk/includes/Security.php (added)
-
trunk/includes/TwoFactor.php (added)
-
trunk/nhrrob-secure.php (modified) (2 diffs)
-
trunk/readme.txt (modified) (4 diffs)
-
trunk/tailwind.config.js (added)
-
trunk/templates (added)
-
trunk/templates/login-2fa-form.php (added)
-
trunk/templates/profile-2fa.php (added)
Legend:
- Unmodified
- Added
- Removed
-
nhrrob-secure/tags/1.0.4/nhrrob-secure.php
r3434007 r3435758 1 1 <?php 2 2 /** 3 * Plugin Name: NHR Secure | Protect Admin, Debug Logs & Limit Logins3 * Plugin Name: NHR Secure – Hide Admin, Limit Login & 2FA 4 4 * Plugin URI: http://wordpress.org/plugins/nhrrob-secure/ 5 5 * Description: Lightweight WordPress security plugin that protects your admin area, hides debug logs, and limits login attempts. Minimal code, maximum protection. 6 6 * Author: Nazmul Hasan Robin 7 7 * Author URI: https://profiles.wordpress.org/nhrrob/ 8 * Version: 1.0. 38 * Version: 1.0.4 9 9 * Requires at least: 6.0 10 10 * Requires PHP: 7.4 … … 16 16 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly 17 17 18 define( 'NHRROB_SECURE_VERSION', '1.0.3' ); 19 define( 'NHRROB_SECURE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );18 // Load Composer autoloader 19 require_once __DIR__ . '/vendor/autoload.php'; 20 20 21 21 /** 22 * Feature List 23 * 1. Limit Login Attempts 24 * 2. Custom Login Page (/hidden-access-52w instead of /wp-login.php) 25 * 3. Protect Sensitive Files (debug.log) 22 * The main plugin class 26 23 */ 24 final class NHRRob_Secure { 27 25 28 /** 29 * ============================ 30 * Helper: Get client IP 31 * ============================ 32 * 33 * - Default: uses REMOTE_ADDR 34 * - Enable proxy detection: 35 * add_filter( 'nhrrob_secure_enable_proxy_ip', '__return_true' ); 36 */ 37 function nhrrob_secure_get_ip() { 38 $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : 'unknown'; 26 /** 27 * Plugin version 28 * 29 * @var string 30 */ 31 const version = '1.0.4'; 39 32 40 $enable_proxy = apply_filters( 'nhrrob_secure_enable_proxy_ip', false ); 33 /** 34 * Class constructor 35 */ 36 private function __construct() { 37 $this->define_constants(); 41 38 42 if ( $enable_proxy ) { 43 if ( ! empty( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) { 44 $ip = sanitize_text_field( wp_unslash( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ); 45 } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { 46 $parts = explode( ',', sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) ); 47 $ip = sanitize_text_field( trim( $parts[0] ) ); 39 add_action( 'plugins_loaded', [ $this, 'init_plugin' ] ); 40 } 41 42 /** 43 * Initialize a singleton instance 44 * 45 * @return \NHRRob_Secure 46 */ 47 public static function init() { 48 static $instance = false; 49 50 if ( ! $instance ) { 51 $instance = new self(); 52 } 53 54 return $instance; 55 } 56 57 /** 58 * Define the required plugin constants 59 * 60 * @return void 61 */ 62 public function define_constants() { 63 define( 'NHRROB_SECURE_VERSION', self::version ); 64 define( 'NHRROB_SECURE_FILE', __FILE__ ); 65 define( 'NHRROB_SECURE_PATH', __DIR__ ); 66 define( 'NHRROB_SECURE_PLUGIN_DIR', plugin_dir_path( NHRROB_SECURE_FILE ) ); 67 define( 'NHRROB_SECURE_URL', plugins_url( '', NHRROB_SECURE_FILE ) ); 68 define( 'NHRROB_SECURE_ASSETS', NHRROB_SECURE_URL . '/assets' ); 69 } 70 71 /** 72 * Initialize the plugin 73 * 74 * @return void 75 */ 76 public function init_plugin() { 77 78 // Initialize security features 79 new \NHRRob\Secure\Security(); 80 81 // Initialize 2FA features 82 new \NHRRob\Secure\TwoFactor(); 83 84 // Initialize assets 85 new \NHRRob\Secure\Assets(); 86 87 // Initialize REST API 88 new \NHRRob\Secure\Admin\Api(); 89 90 // Initialize admin menu 91 if ( is_admin() ) { 92 new \NHRRob\Secure\Admin(); 48 93 } 49 94 } 50 51 return apply_filters( 'nhrrob_secure_get_ip', $ip );52 95 } 53 96 97 54 98 /** 55 * ============================ 56 * Helper: Get limit login failed and block transients 57 * ============================ 99 * Initializes the main plugin 58 100 * 59 * @param string $username The username of the user. 60 * @return array The array contains the failed_key, block_key, failed_value, and block_value. 101 * @return \NHRRob_Secure 61 102 */ 62 function nhrrob_secure_get_limit_login_transients( $username ) { 63 $ip = nhrrob_secure_get_ip(); 64 $username_clean = is_string( $username ) ? strtolower( sanitize_user( $username, true ) ) : 'unknown'; 65 $md5 = md5( $ip . '|' . $username_clean ); 66 67 $failed_key = 'nhrrob_secure_failed_' . $md5; 68 $failed_value = (int) get_transient( $failed_key ); 69 $block_key = 'nhrrob_secure_block_' . $md5; 70 $block_value = get_transient( $block_key ); 71 72 return [ 73 'failed_key' => $failed_key, 74 'block_key' => $block_key, 75 'failed_value' => $failed_value, 76 'block_value' => $block_value, 77 ]; 103 function nhrrob_secure() { 104 return NHRRob_Secure::init(); 78 105 } 79 106 80 /** 81 * ============================ 82 * Helper: Render 404 Page 83 * ============================ 84 */ 85 function nhrrob_secure_render_404() { 86 wp_safe_redirect( home_url( '404' ) ); 87 exit; 88 } 89 90 /** 91 * ============================================================ 92 * 1. LIMIT LOGIN ATTEMPTS (IP + Username) 93 * ============================================================ 94 * 95 * Tracks failed login attempts and blocks IP addresses that exceed the limit. 96 * 97 * Usage: 98 * - Change the maximum allowed login attempts: 99 * add_filter( 'nhrrob_secure_login_attempts_limit', fn() => 10 ); 100 * - Turn off the feature: 101 * add_filter( 'nhrrob_secure_limit_login_attempts', fn() => false ); 102 */ 103 function nhrrob_secure_limit_login_attempts_init() { 104 if ( ! apply_filters( 'nhrrob_secure_limit_login_attempts', true ) ) { 105 return; 106 } 107 108 add_action( 'wp_login_failed', function( $username ) { 109 $transients = nhrrob_secure_get_limit_login_transients( $username ); 110 $failed_value = (int) $transients['failed_value'] + 1; 111 112 set_transient( $transients['failed_key'], $failed_value, HOUR_IN_SECONDS ); 113 114 $limit = (int) apply_filters( 'nhrrob_secure_login_attempts_limit', 5 ); 115 116 if ( $failed_value >= $limit ) { 117 set_transient( $transients['block_key'], true, 2 * HOUR_IN_SECONDS ); 118 } 119 }); 120 121 add_action( 'wp_login', function( $user_login ) { 122 $transients = nhrrob_secure_get_limit_login_transients( $user_login ); 123 delete_transient( $transients['failed_key'] ); 124 delete_transient( $transients['block_key'] ); 125 }); 126 127 add_filter( 'authenticate', function( $user, $username = null, $password = null ) { 128 $username_clean = is_string( $username ) ? strtolower( sanitize_user( $username, true ) ) : ''; 129 if ( empty( $username_clean ) ) { 130 return $user; 131 } 132 133 $transients = nhrrob_secure_get_limit_login_transients( $username_clean ); 134 135 if ( $transients['block_value'] ) { 136 return new WP_Error( 137 'nhrrob_secure_blocked', 138 __( 'Too many failed login attempts for this account from your IP. Try again later.', 'nhrrob-secure' ) 139 ); 140 } 141 142 return $user; 143 }, 30, 3 ); 144 } 145 146 add_action( 'init', 'nhrrob_secure_limit_login_attempts_init' ); 147 148 /** 149 * ============================================================ 150 * 2. CUSTOM LOGIN PAGE 151 * ============================================================ 152 * 153 * Changes default login URL from /wp-login.php to /hidden-access-52w 154 * 155 * Usage: 156 * - Change the custom login URL: 157 * add_filter( 'nhrrob_secure_custom_login_url', fn() => '/my-custom-login' ); 158 * - Turn off the feature: 159 * add_filter( 'nhrrob_secure_custom_login_page', '__return_false' ); 160 */ 161 function nhrrob_secure_custom_login_page_init() { 162 if ( ! apply_filters( 'nhrrob_secure_custom_login_page', true ) ) { 163 return; 164 } 165 166 // Block direct access to wp-login.php 167 $script_name = isset( $_SERVER['SCRIPT_NAME'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SCRIPT_NAME'] ) ) : ''; 168 if ( strpos( $script_name, '/wp-login.php' ) !== false ) { 169 nhrrob_secure_render_404(); 170 } 171 172 // Block direct access to wp-admin for guests 173 add_action( 'init', function() { 174 $script_name = isset( $_SERVER['SCRIPT_NAME'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SCRIPT_NAME'] ) ) : ''; 175 176 if ( is_admin() && ! is_user_logged_in() && ! defined( 'DOING_AJAX' ) && ! defined( 'DOING_CRON' ) ) { 177 // Allow admin-post.php for frontend form submissions 178 if ( strpos( $script_name, 'admin-post.php' ) === false ) { 179 nhrrob_secure_render_404(); 180 } 181 } 182 }); 183 184 // Handle custom login URL (use template_redirect for proper WordPress context) 185 add_action( 'template_redirect', function() { 186 $custom_login_url = apply_filters( 'nhrrob_secure_custom_login_url', '/hidden-access-52w' ); 187 $custom_login_url = trim( $custom_login_url, '/' ); 188 $custom_login_url = '/' . ltrim( $custom_login_url, '/' ); 189 190 $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; 191 $parsed_url = wp_parse_url( $request_uri ); 192 $path = isset( $parsed_url['path'] ) ? $parsed_url['path'] : ''; 193 194 // Normalize path (remove trailing slash for comparison) 195 $path_normalized = rtrim( $path, '/' ); 196 $custom_login_url_normalized = rtrim( $custom_login_url, '/' ); 197 198 // Check if request is for custom login URL 199 if ( $path_normalized === $custom_login_url_normalized || $path === $custom_login_url || $path === $custom_login_url . '/' ) { 200 // Preserve query string 201 $query_string = isset( $parsed_url['query'] ) ? '?' . $parsed_url['query'] : ''; 202 203 // Temporarily modify REQUEST_URI to load wp-login.php 204 $_SERVER['REQUEST_URI'] = '/wp-login.php' . $query_string; 205 206 // Bring globals into scope for wp-login.php 207 global $error, $interim_login, $action, $wp_error, $user_login; 208 209 // Override 404 status (since WP thinks this slug doesn't exist) 210 if ( function_exists( 'status_header' ) ) { 211 status_header( 200 ); 212 } 213 if ( function_exists( 'nocache_headers' ) ) { 214 nocache_headers(); 215 } 216 217 218 219 // Load WordPress login 220 require_once( ABSPATH . 'wp-login.php' ); 221 222 223 224 exit; 225 } 226 }, 1 ); 227 228 // Rewrite wp-login.php URLs to custom login URL 229 add_filter( 'site_url', function( $url, $path, $scheme ) { 230 if ( strpos( $url, 'wp-login.php' ) !== false ) { 231 $custom_login_url = apply_filters( 'nhrrob_secure_custom_login_url', '/hidden-access-52w' ); 232 $custom_login_url = trim( $custom_login_url, '/' ); 233 $url = str_replace( 'wp-login.php', $custom_login_url, $url ); 234 $url = str_replace( '//' . $custom_login_url, '/' . $custom_login_url, $url ); // fix potential double slash if any 235 } 236 return $url; 237 }, 10, 3 ); 238 } 239 240 add_action( 'init', 'nhrrob_secure_custom_login_page_init', 0 ); 241 242 /** 243 * ============================================================ 244 * 3. PROTECT DEBUG LOG FILE 245 * ============================================================ 246 * 247 * Blocks direct access to /wp-content/debug.log 248 * Shows 403 Forbidden for all users 249 * 250 * Usage: 251 * - Turn off the feature: 252 * add_filter( 'nhrrob_secure_protect_debug_log', '__return_false' ); 253 */ 254 function nhrrob_secure_protect_debug_log_init() { 255 if ( ! apply_filters( 'nhrrob_secure_protect_debug_log', true ) ) { 256 return; 257 } 258 259 // Check early to catch direct file access 260 add_action( 'plugins_loaded', function() { 261 $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; 262 $parsed_url = wp_parse_url( $request_uri ); 263 $path = isset( $parsed_url['path'] ) ? $parsed_url['path'] : ''; 264 265 // Check if request is for debug.log in wp-content directory 266 if ( strpos( $path, '/wp-content/debug.log' ) !== false || 267 ( strpos( $path, 'debug.log' ) !== false && strpos( $path, 'wp-content' ) !== false ) ) { 268 if ( function_exists( 'status_header' ) ) { 269 status_header( 403 ); 270 } else { 271 http_response_code( 403 ); 272 } 273 if ( function_exists( 'nocache_headers' ) ) { 274 nocache_headers(); 275 } 276 header( 'Content-Type: text/html; charset=utf-8' ); 277 die( '403 Forbidden' ); 278 } 279 }, 1 ); 280 281 // Also check in template_redirect as backup 282 add_action( 'template_redirect', function() { 283 $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; 284 $parsed_url = wp_parse_url( $request_uri ); 285 $path = isset( $parsed_url['path'] ) ? $parsed_url['path'] : ''; 286 287 // Check if request is for debug.log in wp-content directory 288 if ( strpos( $path, '/wp-content/debug.log' ) !== false || 289 ( strpos( $path, 'debug.log' ) !== false && strpos( $path, 'wp-content' ) !== false ) ) { 290 status_header( 403 ); 291 nocache_headers(); 292 header( 'Content-Type: text/html; charset=utf-8' ); 293 die( '403 Forbidden' ); 294 } 295 }, 1 ); 296 } 297 298 add_action( 'init', 'nhrrob_secure_protect_debug_log_init', 0 ); 299 300 /** 301 * Enable/Disable Features 302 * Example usages are shown below 303 */ 304 305 // Turn off limit login attempts 306 // add_filter( 'nhrrob_secure_limit_login_attempts', '__return_false' ); 307 308 // Turn off custom login page 309 // add_filter( 'nhrrob_secure_custom_login_page', '__return_false' ); 310 311 // Turn off debug log protection 312 // add_filter( 'nhrrob_secure_protect_debug_log', '__return_false' ); 107 // Call the plugin 108 nhrrob_secure(); -
nhrrob-secure/tags/1.0.4/readme.txt
r3434007 r3435758 1 === NHR Secure | Protect Admin, Debug Logs & Limit Logins===1 === NHR Secure – Hide Admin, Limit Login & 2FA === 2 2 Contributors: nhrrob 3 Tags: security, admin, login, debug, protection3 Tags: security, hide admin, login protection, debug log, 2fa 4 4 Requires at least: 6.0 5 5 Tested up to: 6.9 6 6 Requires PHP: 7.4 7 Stable tag: 1.0. 37 Stable tag: 1.0.4 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html 10 10 11 A lightweight WordPress security plugin that protects your admin area, hides debug logs, and limits login attempts. 12 11 A lightweight WordPress security plugin to protect your admin area with a custom login URL, hide debug logs, limit login attempts, and add 2FA. 13 12 == Description == 14 13 … … 18 17 - Limit login attempts to prevent brute-force attacks. 19 18 - Hide debug logs to prevent sensitive information disclosure. 19 - Add 2FA to your WordPress site. 20 20 21 21 **Features at a glance:** 22 22 23 ### ⚡Limit Login Attempts23 ### 🔒 Limit Login Attempts 24 24 Stop brute-force attacks by temporarily blocking IPs after repeated failed login attempts. 25 - Configurable attempt limit (1-20, default: 5) 26 - Blocks based on IP + Username combination 27 - Auto-unblock after 2 hours 25 28 26 ### 🌟 Lightweight & Minimal 27 Designed to deliver maximum security with minimal code. No complex settings or configuration needed. 29 ### 🔐 Custom Login Page 30 Hide wp-login.php and use a custom login URL. 31 - Default custom URL: `/hidden-access-52w` 32 - Blocks direct access to wp-login.php and wp-admin for guests 28 33 29 ### 💬 Simple & Effective 30 Install, activate, and your site is protected instantly. 34 ### 🛡️ Protect Debug Log File 35 Blocks direct access to `/wp-content/debug.log` 36 - Returns 403 Forbidden for all users 37 38 ### ⚙️ Modern Settings Page 39 Configure everything from a beautiful React-powered interface. 40 - Located under **Tools → NHR Secure** 41 - Enable/disable each feature 42 43 ### 🔐 Two-Factor Authentication (2FA) 44 Enable two-factor authentication for users. 45 - QR code is generated and displayed on the user profile page 46 - User must enter the code from their 2FA app to login 47 48 ### ⚡ Lightweight & Minimal 49 Designed to deliver maximum security with minimal code. No bloat, no complexity. 50 - Compatible with most WordPress themes and plugins. 31 51 32 52 == Installation == … … 34 54 1. Upload the `nhrrob-secure` plugin folder to your `/wp-content/plugins/` directory. 35 55 2. Activate the plugin through the 'Plugins' menu in WordPress. 36 3. Protection starts automatically — no configuration required. 37 56 3. Navigate to **Tools → NHR Secure** to configure settings. 38 57 39 58 == Frequently Asked Questions == 40 59 60 = How do I access the settings page? = 61 Navigate to **Tools → NHR Secure** in your WordPress admin dashboard. 62 41 63 = Does it limit login attempts? = 42 Yes. Repeated failed login attempts from the same IP will be temporarily blocked to prevent brute-force attacks. 64 Yes. Repeated failed login attempts from the same IP will be temporarily blocked to prevent brute-force attacks. You can configure the limit (1-20 attempts) from the settings page. 43 65 44 = Do I need other plugins? =45 No. NHR Secure is standalone and works independently.66 = What is the default custom login URL? = 67 The default custom login URL is `/hidden-access-52w`. You can change this in the settings page under Tools → NHR Secure. 46 68 47 = Will it affect my site performance? =48 No. NHR Secure is lightweight and designed to have minimal impact on your WordPress performance.69 = How does 2FA work? = 70 2FA (Two-Factor Authentication) adds an extra layer of security to your WordPress site. When enabled, users must enter a code from their 2FA app (e.g., Google Authenticator, Authy) in addition to their username and password to log in. 49 71 72 = Can I disable specific features? = 73 Yes. You can enable or disable each feature from the settings page under Tools → NHR Secure. 50 74 51 75 == Screenshots == … … 53 77 1. Failed login attempts are blocked. 54 78 2. Custom login page. 55 3. /wp-login.php or /wp-admin goes to 404. 56 4. Debug log is hidden. 79 3. Debug log is hidden. 80 4. Modern React-powered settings page. 81 5. Modern React-powered settings page - part 2. 82 6. 2FA setup in user profile. 57 83 58 84 59 85 == Changelog == 86 87 = 1.0.4 - 09/01/2026 = 88 - Added: Modern React-powered settings page under Tools → NHR Secure 89 - Added: Enable/disable all features from admin interface 90 - Added: Configurable login attempts limit (1-20) 91 - Added: Customizable login page URL from settings 92 - Added: Two-factor authentication (2FA) feature 60 93 61 94 = 1.0.3 - 05/01/2026 = -
nhrrob-secure/trunk/nhrrob-secure.php
r3434007 r3435758 1 1 <?php 2 2 /** 3 * Plugin Name: NHR Secure | Protect Admin, Debug Logs & Limit Logins3 * Plugin Name: NHR Secure – Hide Admin, Limit Login & 2FA 4 4 * Plugin URI: http://wordpress.org/plugins/nhrrob-secure/ 5 5 * Description: Lightweight WordPress security plugin that protects your admin area, hides debug logs, and limits login attempts. Minimal code, maximum protection. 6 6 * Author: Nazmul Hasan Robin 7 7 * Author URI: https://profiles.wordpress.org/nhrrob/ 8 * Version: 1.0. 38 * Version: 1.0.4 9 9 * Requires at least: 6.0 10 10 * Requires PHP: 7.4 … … 16 16 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly 17 17 18 define( 'NHRROB_SECURE_VERSION', '1.0.3' ); 19 define( 'NHRROB_SECURE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );18 // Load Composer autoloader 19 require_once __DIR__ . '/vendor/autoload.php'; 20 20 21 21 /** 22 * Feature List 23 * 1. Limit Login Attempts 24 * 2. Custom Login Page (/hidden-access-52w instead of /wp-login.php) 25 * 3. Protect Sensitive Files (debug.log) 22 * The main plugin class 26 23 */ 24 final class NHRRob_Secure { 27 25 28 /** 29 * ============================ 30 * Helper: Get client IP 31 * ============================ 32 * 33 * - Default: uses REMOTE_ADDR 34 * - Enable proxy detection: 35 * add_filter( 'nhrrob_secure_enable_proxy_ip', '__return_true' ); 36 */ 37 function nhrrob_secure_get_ip() { 38 $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : 'unknown'; 26 /** 27 * Plugin version 28 * 29 * @var string 30 */ 31 const version = '1.0.4'; 39 32 40 $enable_proxy = apply_filters( 'nhrrob_secure_enable_proxy_ip', false ); 33 /** 34 * Class constructor 35 */ 36 private function __construct() { 37 $this->define_constants(); 41 38 42 if ( $enable_proxy ) { 43 if ( ! empty( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) { 44 $ip = sanitize_text_field( wp_unslash( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ); 45 } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { 46 $parts = explode( ',', sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) ); 47 $ip = sanitize_text_field( trim( $parts[0] ) ); 39 add_action( 'plugins_loaded', [ $this, 'init_plugin' ] ); 40 } 41 42 /** 43 * Initialize a singleton instance 44 * 45 * @return \NHRRob_Secure 46 */ 47 public static function init() { 48 static $instance = false; 49 50 if ( ! $instance ) { 51 $instance = new self(); 52 } 53 54 return $instance; 55 } 56 57 /** 58 * Define the required plugin constants 59 * 60 * @return void 61 */ 62 public function define_constants() { 63 define( 'NHRROB_SECURE_VERSION', self::version ); 64 define( 'NHRROB_SECURE_FILE', __FILE__ ); 65 define( 'NHRROB_SECURE_PATH', __DIR__ ); 66 define( 'NHRROB_SECURE_PLUGIN_DIR', plugin_dir_path( NHRROB_SECURE_FILE ) ); 67 define( 'NHRROB_SECURE_URL', plugins_url( '', NHRROB_SECURE_FILE ) ); 68 define( 'NHRROB_SECURE_ASSETS', NHRROB_SECURE_URL . '/assets' ); 69 } 70 71 /** 72 * Initialize the plugin 73 * 74 * @return void 75 */ 76 public function init_plugin() { 77 78 // Initialize security features 79 new \NHRRob\Secure\Security(); 80 81 // Initialize 2FA features 82 new \NHRRob\Secure\TwoFactor(); 83 84 // Initialize assets 85 new \NHRRob\Secure\Assets(); 86 87 // Initialize REST API 88 new \NHRRob\Secure\Admin\Api(); 89 90 // Initialize admin menu 91 if ( is_admin() ) { 92 new \NHRRob\Secure\Admin(); 48 93 } 49 94 } 50 51 return apply_filters( 'nhrrob_secure_get_ip', $ip );52 95 } 53 96 97 54 98 /** 55 * ============================ 56 * Helper: Get limit login failed and block transients 57 * ============================ 99 * Initializes the main plugin 58 100 * 59 * @param string $username The username of the user. 60 * @return array The array contains the failed_key, block_key, failed_value, and block_value. 101 * @return \NHRRob_Secure 61 102 */ 62 function nhrrob_secure_get_limit_login_transients( $username ) { 63 $ip = nhrrob_secure_get_ip(); 64 $username_clean = is_string( $username ) ? strtolower( sanitize_user( $username, true ) ) : 'unknown'; 65 $md5 = md5( $ip . '|' . $username_clean ); 66 67 $failed_key = 'nhrrob_secure_failed_' . $md5; 68 $failed_value = (int) get_transient( $failed_key ); 69 $block_key = 'nhrrob_secure_block_' . $md5; 70 $block_value = get_transient( $block_key ); 71 72 return [ 73 'failed_key' => $failed_key, 74 'block_key' => $block_key, 75 'failed_value' => $failed_value, 76 'block_value' => $block_value, 77 ]; 103 function nhrrob_secure() { 104 return NHRRob_Secure::init(); 78 105 } 79 106 80 /** 81 * ============================ 82 * Helper: Render 404 Page 83 * ============================ 84 */ 85 function nhrrob_secure_render_404() { 86 wp_safe_redirect( home_url( '404' ) ); 87 exit; 88 } 89 90 /** 91 * ============================================================ 92 * 1. LIMIT LOGIN ATTEMPTS (IP + Username) 93 * ============================================================ 94 * 95 * Tracks failed login attempts and blocks IP addresses that exceed the limit. 96 * 97 * Usage: 98 * - Change the maximum allowed login attempts: 99 * add_filter( 'nhrrob_secure_login_attempts_limit', fn() => 10 ); 100 * - Turn off the feature: 101 * add_filter( 'nhrrob_secure_limit_login_attempts', fn() => false ); 102 */ 103 function nhrrob_secure_limit_login_attempts_init() { 104 if ( ! apply_filters( 'nhrrob_secure_limit_login_attempts', true ) ) { 105 return; 106 } 107 108 add_action( 'wp_login_failed', function( $username ) { 109 $transients = nhrrob_secure_get_limit_login_transients( $username ); 110 $failed_value = (int) $transients['failed_value'] + 1; 111 112 set_transient( $transients['failed_key'], $failed_value, HOUR_IN_SECONDS ); 113 114 $limit = (int) apply_filters( 'nhrrob_secure_login_attempts_limit', 5 ); 115 116 if ( $failed_value >= $limit ) { 117 set_transient( $transients['block_key'], true, 2 * HOUR_IN_SECONDS ); 118 } 119 }); 120 121 add_action( 'wp_login', function( $user_login ) { 122 $transients = nhrrob_secure_get_limit_login_transients( $user_login ); 123 delete_transient( $transients['failed_key'] ); 124 delete_transient( $transients['block_key'] ); 125 }); 126 127 add_filter( 'authenticate', function( $user, $username = null, $password = null ) { 128 $username_clean = is_string( $username ) ? strtolower( sanitize_user( $username, true ) ) : ''; 129 if ( empty( $username_clean ) ) { 130 return $user; 131 } 132 133 $transients = nhrrob_secure_get_limit_login_transients( $username_clean ); 134 135 if ( $transients['block_value'] ) { 136 return new WP_Error( 137 'nhrrob_secure_blocked', 138 __( 'Too many failed login attempts for this account from your IP. Try again later.', 'nhrrob-secure' ) 139 ); 140 } 141 142 return $user; 143 }, 30, 3 ); 144 } 145 146 add_action( 'init', 'nhrrob_secure_limit_login_attempts_init' ); 147 148 /** 149 * ============================================================ 150 * 2. CUSTOM LOGIN PAGE 151 * ============================================================ 152 * 153 * Changes default login URL from /wp-login.php to /hidden-access-52w 154 * 155 * Usage: 156 * - Change the custom login URL: 157 * add_filter( 'nhrrob_secure_custom_login_url', fn() => '/my-custom-login' ); 158 * - Turn off the feature: 159 * add_filter( 'nhrrob_secure_custom_login_page', '__return_false' ); 160 */ 161 function nhrrob_secure_custom_login_page_init() { 162 if ( ! apply_filters( 'nhrrob_secure_custom_login_page', true ) ) { 163 return; 164 } 165 166 // Block direct access to wp-login.php 167 $script_name = isset( $_SERVER['SCRIPT_NAME'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SCRIPT_NAME'] ) ) : ''; 168 if ( strpos( $script_name, '/wp-login.php' ) !== false ) { 169 nhrrob_secure_render_404(); 170 } 171 172 // Block direct access to wp-admin for guests 173 add_action( 'init', function() { 174 $script_name = isset( $_SERVER['SCRIPT_NAME'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SCRIPT_NAME'] ) ) : ''; 175 176 if ( is_admin() && ! is_user_logged_in() && ! defined( 'DOING_AJAX' ) && ! defined( 'DOING_CRON' ) ) { 177 // Allow admin-post.php for frontend form submissions 178 if ( strpos( $script_name, 'admin-post.php' ) === false ) { 179 nhrrob_secure_render_404(); 180 } 181 } 182 }); 183 184 // Handle custom login URL (use template_redirect for proper WordPress context) 185 add_action( 'template_redirect', function() { 186 $custom_login_url = apply_filters( 'nhrrob_secure_custom_login_url', '/hidden-access-52w' ); 187 $custom_login_url = trim( $custom_login_url, '/' ); 188 $custom_login_url = '/' . ltrim( $custom_login_url, '/' ); 189 190 $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; 191 $parsed_url = wp_parse_url( $request_uri ); 192 $path = isset( $parsed_url['path'] ) ? $parsed_url['path'] : ''; 193 194 // Normalize path (remove trailing slash for comparison) 195 $path_normalized = rtrim( $path, '/' ); 196 $custom_login_url_normalized = rtrim( $custom_login_url, '/' ); 197 198 // Check if request is for custom login URL 199 if ( $path_normalized === $custom_login_url_normalized || $path === $custom_login_url || $path === $custom_login_url . '/' ) { 200 // Preserve query string 201 $query_string = isset( $parsed_url['query'] ) ? '?' . $parsed_url['query'] : ''; 202 203 // Temporarily modify REQUEST_URI to load wp-login.php 204 $_SERVER['REQUEST_URI'] = '/wp-login.php' . $query_string; 205 206 // Bring globals into scope for wp-login.php 207 global $error, $interim_login, $action, $wp_error, $user_login; 208 209 // Override 404 status (since WP thinks this slug doesn't exist) 210 if ( function_exists( 'status_header' ) ) { 211 status_header( 200 ); 212 } 213 if ( function_exists( 'nocache_headers' ) ) { 214 nocache_headers(); 215 } 216 217 218 219 // Load WordPress login 220 require_once( ABSPATH . 'wp-login.php' ); 221 222 223 224 exit; 225 } 226 }, 1 ); 227 228 // Rewrite wp-login.php URLs to custom login URL 229 add_filter( 'site_url', function( $url, $path, $scheme ) { 230 if ( strpos( $url, 'wp-login.php' ) !== false ) { 231 $custom_login_url = apply_filters( 'nhrrob_secure_custom_login_url', '/hidden-access-52w' ); 232 $custom_login_url = trim( $custom_login_url, '/' ); 233 $url = str_replace( 'wp-login.php', $custom_login_url, $url ); 234 $url = str_replace( '//' . $custom_login_url, '/' . $custom_login_url, $url ); // fix potential double slash if any 235 } 236 return $url; 237 }, 10, 3 ); 238 } 239 240 add_action( 'init', 'nhrrob_secure_custom_login_page_init', 0 ); 241 242 /** 243 * ============================================================ 244 * 3. PROTECT DEBUG LOG FILE 245 * ============================================================ 246 * 247 * Blocks direct access to /wp-content/debug.log 248 * Shows 403 Forbidden for all users 249 * 250 * Usage: 251 * - Turn off the feature: 252 * add_filter( 'nhrrob_secure_protect_debug_log', '__return_false' ); 253 */ 254 function nhrrob_secure_protect_debug_log_init() { 255 if ( ! apply_filters( 'nhrrob_secure_protect_debug_log', true ) ) { 256 return; 257 } 258 259 // Check early to catch direct file access 260 add_action( 'plugins_loaded', function() { 261 $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; 262 $parsed_url = wp_parse_url( $request_uri ); 263 $path = isset( $parsed_url['path'] ) ? $parsed_url['path'] : ''; 264 265 // Check if request is for debug.log in wp-content directory 266 if ( strpos( $path, '/wp-content/debug.log' ) !== false || 267 ( strpos( $path, 'debug.log' ) !== false && strpos( $path, 'wp-content' ) !== false ) ) { 268 if ( function_exists( 'status_header' ) ) { 269 status_header( 403 ); 270 } else { 271 http_response_code( 403 ); 272 } 273 if ( function_exists( 'nocache_headers' ) ) { 274 nocache_headers(); 275 } 276 header( 'Content-Type: text/html; charset=utf-8' ); 277 die( '403 Forbidden' ); 278 } 279 }, 1 ); 280 281 // Also check in template_redirect as backup 282 add_action( 'template_redirect', function() { 283 $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; 284 $parsed_url = wp_parse_url( $request_uri ); 285 $path = isset( $parsed_url['path'] ) ? $parsed_url['path'] : ''; 286 287 // Check if request is for debug.log in wp-content directory 288 if ( strpos( $path, '/wp-content/debug.log' ) !== false || 289 ( strpos( $path, 'debug.log' ) !== false && strpos( $path, 'wp-content' ) !== false ) ) { 290 status_header( 403 ); 291 nocache_headers(); 292 header( 'Content-Type: text/html; charset=utf-8' ); 293 die( '403 Forbidden' ); 294 } 295 }, 1 ); 296 } 297 298 add_action( 'init', 'nhrrob_secure_protect_debug_log_init', 0 ); 299 300 /** 301 * Enable/Disable Features 302 * Example usages are shown below 303 */ 304 305 // Turn off limit login attempts 306 // add_filter( 'nhrrob_secure_limit_login_attempts', '__return_false' ); 307 308 // Turn off custom login page 309 // add_filter( 'nhrrob_secure_custom_login_page', '__return_false' ); 310 311 // Turn off debug log protection 312 // add_filter( 'nhrrob_secure_protect_debug_log', '__return_false' ); 107 // Call the plugin 108 nhrrob_secure(); -
nhrrob-secure/trunk/readme.txt
r3434007 r3435758 1 === NHR Secure | Protect Admin, Debug Logs & Limit Logins===1 === NHR Secure – Hide Admin, Limit Login & 2FA === 2 2 Contributors: nhrrob 3 Tags: security, admin, login, debug, protection3 Tags: security, hide admin, login protection, debug log, 2fa 4 4 Requires at least: 6.0 5 5 Tested up to: 6.9 6 6 Requires PHP: 7.4 7 Stable tag: 1.0. 37 Stable tag: 1.0.4 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html 10 10 11 A lightweight WordPress security plugin that protects your admin area, hides debug logs, and limits login attempts. 12 11 A lightweight WordPress security plugin to protect your admin area with a custom login URL, hide debug logs, limit login attempts, and add 2FA. 13 12 == Description == 14 13 … … 18 17 - Limit login attempts to prevent brute-force attacks. 19 18 - Hide debug logs to prevent sensitive information disclosure. 19 - Add 2FA to your WordPress site. 20 20 21 21 **Features at a glance:** 22 22 23 ### ⚡Limit Login Attempts23 ### 🔒 Limit Login Attempts 24 24 Stop brute-force attacks by temporarily blocking IPs after repeated failed login attempts. 25 - Configurable attempt limit (1-20, default: 5) 26 - Blocks based on IP + Username combination 27 - Auto-unblock after 2 hours 25 28 26 ### 🌟 Lightweight & Minimal 27 Designed to deliver maximum security with minimal code. No complex settings or configuration needed. 29 ### 🔐 Custom Login Page 30 Hide wp-login.php and use a custom login URL. 31 - Default custom URL: `/hidden-access-52w` 32 - Blocks direct access to wp-login.php and wp-admin for guests 28 33 29 ### 💬 Simple & Effective 30 Install, activate, and your site is protected instantly. 34 ### 🛡️ Protect Debug Log File 35 Blocks direct access to `/wp-content/debug.log` 36 - Returns 403 Forbidden for all users 37 38 ### ⚙️ Modern Settings Page 39 Configure everything from a beautiful React-powered interface. 40 - Located under **Tools → NHR Secure** 41 - Enable/disable each feature 42 43 ### 🔐 Two-Factor Authentication (2FA) 44 Enable two-factor authentication for users. 45 - QR code is generated and displayed on the user profile page 46 - User must enter the code from their 2FA app to login 47 48 ### ⚡ Lightweight & Minimal 49 Designed to deliver maximum security with minimal code. No bloat, no complexity. 50 - Compatible with most WordPress themes and plugins. 31 51 32 52 == Installation == … … 34 54 1. Upload the `nhrrob-secure` plugin folder to your `/wp-content/plugins/` directory. 35 55 2. Activate the plugin through the 'Plugins' menu in WordPress. 36 3. Protection starts automatically — no configuration required. 37 56 3. Navigate to **Tools → NHR Secure** to configure settings. 38 57 39 58 == Frequently Asked Questions == 40 59 60 = How do I access the settings page? = 61 Navigate to **Tools → NHR Secure** in your WordPress admin dashboard. 62 41 63 = Does it limit login attempts? = 42 Yes. Repeated failed login attempts from the same IP will be temporarily blocked to prevent brute-force attacks. 64 Yes. Repeated failed login attempts from the same IP will be temporarily blocked to prevent brute-force attacks. You can configure the limit (1-20 attempts) from the settings page. 43 65 44 = Do I need other plugins? =45 No. NHR Secure is standalone and works independently.66 = What is the default custom login URL? = 67 The default custom login URL is `/hidden-access-52w`. You can change this in the settings page under Tools → NHR Secure. 46 68 47 = Will it affect my site performance? =48 No. NHR Secure is lightweight and designed to have minimal impact on your WordPress performance.69 = How does 2FA work? = 70 2FA (Two-Factor Authentication) adds an extra layer of security to your WordPress site. When enabled, users must enter a code from their 2FA app (e.g., Google Authenticator, Authy) in addition to their username and password to log in. 49 71 72 = Can I disable specific features? = 73 Yes. You can enable or disable each feature from the settings page under Tools → NHR Secure. 50 74 51 75 == Screenshots == … … 53 77 1. Failed login attempts are blocked. 54 78 2. Custom login page. 55 3. /wp-login.php or /wp-admin goes to 404. 56 4. Debug log is hidden. 79 3. Debug log is hidden. 80 4. Modern React-powered settings page. 81 5. Modern React-powered settings page - part 2. 82 6. 2FA setup in user profile. 57 83 58 84 59 85 == Changelog == 86 87 = 1.0.4 - 09/01/2026 = 88 - Added: Modern React-powered settings page under Tools → NHR Secure 89 - Added: Enable/disable all features from admin interface 90 - Added: Configurable login attempts limit (1-20) 91 - Added: Customizable login page URL from settings 92 - Added: Two-factor authentication (2FA) feature 60 93 61 94 = 1.0.3 - 05/01/2026 =
Note: See TracChangeset
for help on using the changeset viewer.