siteguard-login-history-table.php
1 month ago
siteguard-menu-admin-filter.php
1 month ago
siteguard-menu-author-query.php
2 weeks ago
siteguard-menu-captcha.php
1 month ago
siteguard-menu-dashboard.php
1 month ago
siteguard-menu-fail-once.php
1 month ago
siteguard-menu-init.php
1 month ago
siteguard-menu-login-alert.php
1 month ago
siteguard-menu-login-history.php
1 month ago
siteguard-menu-login-lock.php
1 month ago
siteguard-menu-protect-xmlrpc.php
1 month ago
siteguard-menu-rename-login.php
2 weeks ago
siteguard-menu-same-error.php
1 month ago
siteguard-menu-updates-notify.php
1 month ago
siteguard-menu-waf-tuning-support.php
1 month ago
siteguard-waf-exclude-rule-table.php
1 month ago
siteguard-menu-rename-login.php
360 lines
| 1 | <?php |
| 2 | |
| 3 | class SiteGuard_Menu_Rename_Login extends SiteGuard_Base { |
| 4 | const OPT_NAME_FEATURE = 'renamelogin_enable'; |
| 5 | const OPT_NAME_FEATURE_REDIRECT = 'redirect_enable'; |
| 6 | const OPT_NAME_RENAME_LOGIN_PATH = 'renamelogin_path'; |
| 7 | const OPT_NAME_OLD_LOGIN_PATH = 'oldlogin_path'; |
| 8 | const OPT_NAME_STUB = 'renamelogin_stub'; |
| 9 | const OPT_NAME_RESCUE = 'rescue_enable'; |
| 10 | |
| 11 | private static $pre_processed = false; |
| 12 | private static $pre_errors = array(); |
| 13 | private static $pre_slug_value = null; |
| 14 | |
| 15 | function __construct() { |
| 16 | $this->render_page(); |
| 17 | } |
| 18 | |
| 19 | private static function get_disallowed_slugs() { |
| 20 | $disallowed = array(); |
| 21 | $files = scandir( ABSPATH ); |
| 22 | if ( false === $files ) { |
| 23 | return array(); |
| 24 | } |
| 25 | |
| 26 | foreach ( $files as $file ) { |
| 27 | // Ignore dot files, current and parent directories. |
| 28 | if ( '.' === $file[0] ) { |
| 29 | continue; |
| 30 | } |
| 31 | |
| 32 | if ( is_dir( ABSPATH . $file ) ) { |
| 33 | $disallowed[] = $file; |
| 34 | } else { |
| 35 | // For files, use the name without extension. |
| 36 | $disallowed[] = pathinfo( $file, PATHINFO_FILENAME ); |
| 37 | } |
| 38 | } |
| 39 | return array_unique( $disallowed ); |
| 40 | } |
| 41 | |
| 42 | private static function stub_abs_path( $slug ) { |
| 43 | $slug = trim( (string) $slug, '/' ); |
| 44 | if ( $slug === '' ) { |
| 45 | $slug = 'login'; } |
| 46 | return trailingslashit( ABSPATH ) . $slug . '.php'; |
| 47 | } |
| 48 | |
| 49 | public static function pre_handle_post() { |
| 50 | if ( ! isset( $_POST['update'] ) || ! check_admin_referer( 'siteguard-menu-rename-login-submit' ) ) { |
| 51 | return; |
| 52 | } |
| 53 | |
| 54 | self::$pre_processed = true; |
| 55 | global $siteguard_config, $siteguard_rename_login; |
| 56 | |
| 57 | $error = false; |
| 58 | $errors = siteguard_check_multisite(); |
| 59 | if ( is_wp_error( $errors ) ) { |
| 60 | self::$pre_errors[] = $errors->get_error_message(); |
| 61 | $error = true; |
| 62 | } |
| 63 | |
| 64 | $posted_feature = isset( $_POST[ self::OPT_NAME_FEATURE ] ) ? sanitize_text_field( $_POST[ self::OPT_NAME_FEATURE ] ) : ''; |
| 65 | if ( false === $error && ( '0' !== $posted_feature && '1' !== $posted_feature ) ) { |
| 66 | self::$pre_errors[] = esc_html__( 'ERROR: Invalid input value.', 'siteguard' ); |
| 67 | $error = true; |
| 68 | } |
| 69 | |
| 70 | if ( false === $error ) { |
| 71 | $posted_slug = sanitize_text_field( $_POST[ self::OPT_NAME_RENAME_LOGIN_PATH ] ); |
| 72 | if ( 1 !== preg_match( '/^[a-zA-Z0-9_-]+$/', $posted_slug ) ) { |
| 73 | self::$pre_errors[] = esc_html__( 'Only letters, numbers, hyphens, and underscores can be used for the login path.', 'siteguard' ); |
| 74 | self::$pre_slug_value = stripslashes( $posted_slug ); |
| 75 | $error = true; |
| 76 | } |
| 77 | |
| 78 | if ( false === $error && strlen( $posted_slug ) < 4 ) { |
| 79 | self::$pre_errors[] = esc_html__( 'The new login path must be at least 4 characters long.', 'siteguard' ); |
| 80 | self::$pre_slug_value = stripslashes( $posted_slug ); |
| 81 | $error = true; |
| 82 | } |
| 83 | |
| 84 | if ( false === $error ) { |
| 85 | $current_slug = $siteguard_config->get( self::OPT_NAME_RENAME_LOGIN_PATH ); |
| 86 | if ( $posted_slug !== $current_slug ) { |
| 87 | $disallowed_slugs = self::get_disallowed_slugs(); |
| 88 | if ( in_array( $posted_slug, $disallowed_slugs, true ) ) { |
| 89 | /* translators: %s: slug */ |
| 90 | self::$pre_errors[] = sprintf( esc_html__( '%s cannot be used for the new login path because it conflicts with an existing file or directory in the WordPress root.', 'siteguard' ), '<code>' . esc_html( $posted_slug ) . '</code>' ); |
| 91 | self::$pre_slug_value = stripslashes( $posted_slug ); |
| 92 | $error = true; |
| 93 | } |
| 94 | } |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | if ( false === $error ) { |
| 99 | $old_feature = $siteguard_config->get( self::OPT_NAME_FEATURE ); |
| 100 | $old_feature_redirect = $siteguard_config->get( self::OPT_NAME_FEATURE_REDIRECT ); |
| 101 | $old_rename_login_path = $siteguard_config->get( self::OPT_NAME_RENAME_LOGIN_PATH ); |
| 102 | $old_old_login_path = $siteguard_config->get( self::OPT_NAME_OLD_LOGIN_PATH ); |
| 103 | $old_rescue = $siteguard_config->get( self::OPT_NAME_RESCUE ); |
| 104 | |
| 105 | $new_feature = $posted_feature; |
| 106 | $new_feature_redirect = isset( $_POST[ self::OPT_NAME_FEATURE_REDIRECT ] ) ? '1' : '0'; |
| 107 | $new_rename_login_path = $posted_slug; |
| 108 | $new_rescue = isset( $_POST[ self::OPT_NAME_RESCUE ] ) ? '1' : '0'; |
| 109 | |
| 110 | $siteguard_config->set( self::OPT_NAME_FEATURE, $new_feature ); |
| 111 | $siteguard_config->set( self::OPT_NAME_FEATURE_REDIRECT, $new_feature_redirect ); |
| 112 | $siteguard_config->set( self::OPT_NAME_RENAME_LOGIN_PATH, $new_rename_login_path ); |
| 113 | $siteguard_config->set( self::OPT_NAME_OLD_LOGIN_PATH, $old_rename_login_path ); |
| 114 | $siteguard_config->set( self::OPT_NAME_RESCUE, $new_rescue ); |
| 115 | $siteguard_config->update(); |
| 116 | |
| 117 | $result = true; |
| 118 | if ( '0' === $new_feature ) { |
| 119 | $result = $siteguard_rename_login->feature_off(); |
| 120 | } else { |
| 121 | $result = $siteguard_rename_login->feature_on(); |
| 122 | $stub_required = ! $siteguard_rename_login->can_use_htaccess(); |
| 123 | $stub_file = self::stub_abs_path( $new_rename_login_path ); |
| 124 | $stub_fail = (bool) get_transient( SiteGuard_RenameLogin::STUB_WRITE_FAIL_TRANSIENT ); |
| 125 | |
| 126 | if ( false === $result || ( $stub_required && ( $stub_fail || ! file_exists( $stub_file ) ) ) ) { |
| 127 | $siteguard_config->set( self::OPT_NAME_FEATURE, $old_feature ); |
| 128 | $siteguard_config->set( self::OPT_NAME_FEATURE_REDIRECT, $old_feature_redirect ); |
| 129 | $siteguard_config->set( self::OPT_NAME_RENAME_LOGIN_PATH, $old_rename_login_path ); |
| 130 | $siteguard_config->set( self::OPT_NAME_OLD_LOGIN_PATH, $old_old_login_path ); |
| 131 | $siteguard_config->set( self::OPT_NAME_RESCUE, $old_rescue ); |
| 132 | $siteguard_config->update(); |
| 133 | SiteGuard_RenameLogin::feature_off(); |
| 134 | $result = false; |
| 135 | |
| 136 | if ( $stub_required || $stub_fail ) { |
| 137 | self::$pre_errors[] = esc_html__( 'ERROR: Failed to enable. WordPress cannot create the required file in the site root.', 'siteguard' ) |
| 138 | . ' ' . esc_html__( 'Please check that the WordPress root directory is writable, or contact your hosting provider.', 'siteguard' ); |
| 139 | } else { |
| 140 | self::$pre_errors[] = esc_html__( 'ERROR: Failed to update settings. Please try again.', 'siteguard' ); |
| 141 | } |
| 142 | } elseif ( '1' === $new_feature ) { |
| 143 | $siteguard_rename_login->send_notify(); |
| 144 | } |
| 145 | |
| 146 | if ( $stub_fail ) { |
| 147 | delete_transient( SiteGuard_RenameLogin::STUB_WRITE_FAIL_TRANSIENT ); |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | if ( true === $result ) { |
| 152 | wp_safe_redirect( admin_url( 'admin.php?page=siteguard_rename_login&updated=1' ) ); |
| 153 | exit; |
| 154 | } |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | private function current_mode_description() { |
| 159 | global $siteguard_config; |
| 160 | |
| 161 | if ( '1' === $siteguard_config->get( self::OPT_NAME_FEATURE ) ) { |
| 162 | $slug = trim( (string) $siteguard_config->get( self::OPT_NAME_RENAME_LOGIN_PATH ), '/' ); |
| 163 | $stub_on = ( SITEGUARD_RENAME_MODE_STUB === $siteguard_config->get( self::OPT_NAME_STUB ) ); |
| 164 | $loginurl = rtrim( site_url(), '/' ) . '/' . ( $stub_on ? ( $slug . '.php' ) : $slug ); |
| 165 | if ( $stub_on ) { |
| 166 | $reason = $this->stub_reason_text(); |
| 167 | return sprintf( |
| 168 | '%s <code>%s</code><br>%s%s', |
| 169 | esc_html__( 'Login URL:', 'siteguard' ), |
| 170 | esc_url( $loginurl ), |
| 171 | esc_html__( 'The login URL ends with .php because this server does not support .htaccess.', 'siteguard' ), |
| 172 | '' !== $reason ? '<br>' . $reason : '' |
| 173 | ); |
| 174 | } else { |
| 175 | return sprintf( |
| 176 | '%s <code>%s</code>', |
| 177 | esc_html__( 'Login URL:', 'siteguard' ), |
| 178 | esc_url( $loginurl ) |
| 179 | ); |
| 180 | } |
| 181 | } |
| 182 | $loginurl = rtrim( site_url(), '/' ) . '/wp-login.php'; |
| 183 | return sprintf( |
| 184 | '%s <code>%s</code><br>%s', |
| 185 | esc_html__( 'Login URL:', 'siteguard' ), |
| 186 | $loginurl, |
| 187 | esc_html__( 'This feature is currently off.', 'siteguard' ) |
| 188 | ); |
| 189 | } |
| 190 | |
| 191 | /** |
| 192 | * Human-readable explanation of why stub (.php) mode was chosen, based on the |
| 193 | * reason recorded by SiteGuard_RenameLogin::feature_on(). Returns '' when no |
| 194 | * reason is available. The returned string may contain pre-escaped HTML. |
| 195 | */ |
| 196 | private function stub_reason_text() { |
| 197 | global $siteguard_config; |
| 198 | $reason = $siteguard_config->get( self::OPT_NAME_STUB . '_reason' ); |
| 199 | if ( ! is_array( $reason ) || empty( $reason['code'] ) ) { |
| 200 | return ''; |
| 201 | } |
| 202 | $url = isset( $reason['url'] ) ? '<code>' . esc_html( $reason['url'] ) . '</code>' : ''; |
| 203 | switch ( $reason['code'] ) { |
| 204 | case 'nginx': |
| 205 | return esc_html__( 'Reason: the server is Nginx, which does not use .htaccess.', 'siteguard' ); |
| 206 | case 'server_software': |
| 207 | return esc_html__( 'Reason: the server is not Apache/LiteSpeed, so .htaccess is not used.', 'siteguard' ); |
| 208 | case 'not_writable': |
| 209 | return esc_html__( 'Reason: the .htaccess file (or the WordPress directory) is not writable.', 'siteguard' ); |
| 210 | case 'mkdir': |
| 211 | return esc_html__( 'Reason: a temporary test directory could not be created in the WordPress directory (check write permission).', 'siteguard' ); |
| 212 | case 'write': |
| 213 | return esc_html__( 'Reason: the temporary test files could not be written.', 'siteguard' ); |
| 214 | case 'htaccess_ignored': |
| 215 | return sprintf( |
| 216 | /* translators: %s: test URL */ |
| 217 | esc_html__( 'Reason: the .htaccess file is present but the server is ignoring it (for example AllowOverride is set to None, or mod_rewrite is not enabled for this directory), so the rewrite for %s had no effect.', 'siteguard' ), |
| 218 | $url |
| 219 | ); |
| 220 | case 'wp_error': |
| 221 | return sprintf( |
| 222 | /* translators: 1: test URL, 2: error message */ |
| 223 | esc_html__( 'Reason: the self-test request to %1$s failed (%2$s). The server may be unable to reach its own URL (loopback).', 'siteguard' ), |
| 224 | $url, |
| 225 | esc_html( isset( $reason['detail'] ) ? $reason['detail'] : '' ) |
| 226 | ); |
| 227 | case 'http_status': |
| 228 | $status = (int) ( isset( $reason['status'] ) ? $reason['status'] : 0 ); |
| 229 | if ( 401 === $status || 403 === $status ) { |
| 230 | $hint = esc_html__( 'It may be blocked by an access restriction such as Basic authentication or an IP restriction.', 'siteguard' ); |
| 231 | } elseif ( 404 === $status ) { |
| 232 | $hint = esc_html__( 'The test URL was not found, for example because WordPress is installed in a subdirectory so the self-test URL does not map to it, or the request is routed elsewhere.', 'siteguard' ); |
| 233 | } elseif ( 429 === $status || 503 === $status ) { |
| 234 | $hint = esc_html__( 'The request was rate-limited or temporarily blocked. This is usually a server-side rate limit or anti-bot/access-control protection (separate from .htaccess), not a sign that .htaccess is broken. Try again after a short while.', 'siteguard' ); |
| 235 | } elseif ( $status >= 300 && $status < 400 ) { |
| 236 | $hint = esc_html__( 'The request was redirected (for example HTTP to HTTPS, or a canonical redirect).', 'siteguard' ); |
| 237 | } else { |
| 238 | $hint = ''; |
| 239 | } |
| 240 | return sprintf( |
| 241 | /* translators: 1: test URL, 2: HTTP status code */ |
| 242 | esc_html__( 'Reason: the self-test request to %1$s returned HTTP %2$s instead of 200.', 'siteguard' ), |
| 243 | $url, |
| 244 | esc_html( (string) $status ) |
| 245 | ) . ( '' !== $hint ? ' ' . $hint : '' ); |
| 246 | case 'bad_body': |
| 247 | return sprintf( |
| 248 | /* translators: %s: test URL */ |
| 249 | esc_html__( 'Reason: the self-test request to %s did not return the expected result; the .htaccess rewrite did not take effect (it may be disabled by AllowOverride or overridden by another rule).', 'siteguard' ), |
| 250 | $url |
| 251 | ); |
| 252 | } |
| 253 | return ''; |
| 254 | } |
| 255 | |
| 256 | function render_page() { |
| 257 | global $siteguard_config; |
| 258 | |
| 259 | $opt_val_feature = $siteguard_config->get( self::OPT_NAME_FEATURE ); |
| 260 | $opt_val_feature_redirect = $siteguard_config->get( self::OPT_NAME_FEATURE_REDIRECT ); |
| 261 | $opt_val_rename_login_path = ( null !== self::$pre_slug_value ) |
| 262 | ? self::$pre_slug_value |
| 263 | : $siteguard_config->get( self::OPT_NAME_RENAME_LOGIN_PATH ); |
| 264 | $opt_val_rescue = $siteguard_config->get( self::OPT_NAME_RESCUE ); |
| 265 | |
| 266 | foreach ( self::$pre_errors as $msg ) { |
| 267 | echo '<div class="error settings-error"><p><strong>' . wp_kses_post( $msg ) . '</strong></p></div>'; |
| 268 | } |
| 269 | |
| 270 | echo '<div class="wrap">'; |
| 271 | echo '<img src="' . SITEGUARD_URL_PATH . 'images/sg_wp_plugin_logo_40.png" alt="SiteGuard Logo" />'; |
| 272 | echo '<h2>' . esc_html__( 'Rename Login', 'siteguard' ) . '</h2>'; |
| 273 | |
| 274 | if ( isset( $_GET['updated'] ) && '1' === $_GET['updated'] ) { |
| 275 | echo '<div class="updated"><p><strong>'; |
| 276 | esc_html_e( 'Options saved.', 'siteguard' ); |
| 277 | echo '</strong></p></div>'; |
| 278 | } |
| 279 | |
| 280 | echo '<div class="notice notice-info" style="padding:8px 12px;margin-top:10px;">' . wp_kses_post( $this->current_mode_description() ) . '</div>'; |
| 281 | |
| 282 | $documentation_link = '<a href="' . esc_url( __( 'https://www.jp-secure.com/siteguard_wp_plugin_en/howto/rename_login/', 'siteguard' ) ) . '" target="_blank">' . esc_html__( 'online documentation', 'siteguard' ) . '</a>'; |
| 283 | echo '<div class="siteguard-description">' |
| 284 | . sprintf( |
| 285 | /* translators: %1$s: Link to the online documentation. */ |
| 286 | esc_html__( 'See the %1$s.', 'siteguard' ), |
| 287 | $documentation_link |
| 288 | ) |
| 289 | . '</div>'; |
| 290 | ?> |
| 291 | <form name="form1" method="post" action=""> |
| 292 | <table class="form-table"> |
| 293 | <tr> |
| 294 | <th scope="row" colspan="2"> |
| 295 | <ul class="siteguard-radios"> |
| 296 | <li> |
| 297 | <input type="radio" name="<?php echo self::OPT_NAME_FEATURE; ?>" id="<?php echo self::OPT_NAME_FEATURE . '_on'; ?>" value="1" <?php checked( $opt_val_feature, '1' ); ?> > |
| 298 | <label for="<?php echo self::OPT_NAME_FEATURE . '_on'; ?>"><?php echo esc_html_e( 'ON', 'siteguard' ); ?></label> |
| 299 | </li><li> |
| 300 | <input type="radio" name="<?php echo self::OPT_NAME_FEATURE; ?>" id="<?php echo self::OPT_NAME_FEATURE . '_off'; ?>" value="0" <?php checked( $opt_val_feature, '0' ); ?> > |
| 301 | <label for="<?php echo self::OPT_NAME_FEATURE . '_off'; ?>"><?php echo esc_html_e( 'OFF', 'siteguard' ); ?></label> |
| 302 | </li> |
| 303 | </ul> |
| 304 | <?php |
| 305 | echo '<p class="description">'; |
| 306 | esc_html_e( 'Works automatically on most servers. If activation fails, please check file permissions or contact your hosting provider.', 'siteguard' ); |
| 307 | echo '</p>'; |
| 308 | ?> |
| 309 | </th> |
| 310 | </tr><tr> |
| 311 | <th scope="row"><label for="<?php echo self::OPT_NAME_RENAME_LOGIN_PATH; ?>"><?php esc_html_e( 'New Login Path', 'siteguard' ); ?></label></th> |
| 312 | <td> |
| 313 | <?php |
| 314 | $stub_on = ( SITEGUARD_RENAME_MODE_STUB === $siteguard_config->get( self::OPT_NAME_STUB ) ); |
| 315 | echo esc_url( site_url() ) . '/'; |
| 316 | ?> |
| 317 | <input type="text" name="<?php echo self::OPT_NAME_RENAME_LOGIN_PATH; ?>" id="<?php echo self::OPT_NAME_RENAME_LOGIN_PATH; ?>" value="<?php echo esc_attr( $opt_val_rename_login_path ); ?>" > |
| 318 | <?php |
| 319 | if ( '1' === $opt_val_feature && $stub_on ) { |
| 320 | echo '.php'; |
| 321 | } |
| 322 | echo '<p class="description">'; |
| 323 | if ( $stub_on ) { |
| 324 | esc_html_e( 'The actual login URL will include .php at the end.', 'siteguard' ); |
| 325 | } else { |
| 326 | esc_html_e( 'Letters, numbers, hyphens, and underscores only.', 'siteguard' ); |
| 327 | } |
| 328 | echo '</p>'; |
| 329 | ?> |
| 330 | </td> |
| 331 | </tr><tr> |
| 332 | <th scope="row"><?php esc_html_e( 'Option', 'siteguard' ); ?></th> |
| 333 | <td> |
| 334 | <p> |
| 335 | <input type="checkbox" name="<?php echo self::OPT_NAME_FEATURE_REDIRECT; ?>" id="<?php echo self::OPT_NAME_FEATURE_REDIRECT; ?>" value="1" <?php checked( $opt_val_feature_redirect, '1' ); ?> > |
| 336 | <label for="<?php echo self::OPT_NAME_FEATURE_REDIRECT; ?>"><?php esc_html_e( 'Do not redirect from the admin page to the login page.', 'siteguard' ); ?></label> |
| 337 | </p> |
| 338 | <p> |
| 339 | <input type="checkbox" name="<?php echo self::OPT_NAME_RESCUE; ?>" id="<?php echo self::OPT_NAME_RESCUE; ?>" value="1" <?php checked( $opt_val_rescue, '1' ); ?> > |
| 340 | <label for="<?php echo self::OPT_NAME_RESCUE; ?>"><?php echo esc_html__( 'Enable login rescue', 'siteguard' ); ?> (<code><?php echo esc_url( add_query_arg( 'siteguard_rescue', '1', site_url( '/' ) ) ); ?></code>)</label> |
| 341 | <br><span class="description"><?php esc_html_e( 'Users can request the current login URL by solving a CAPTCHA. For security, a confirmation is always shown regardless of whether the address exists.', 'siteguard' ); ?></span> |
| 342 | </p> |
| 343 | </td> |
| 344 | </tr> |
| 345 | </table> |
| 346 | <input type="hidden" name="update" value="Y"> |
| 347 | <div class="siteguard-description"> |
| 348 | <?php esc_html_e( 'This protects your site by changing the login page URL, making it harder for automated attacks to find it. The default URL is “login_<5 random digits>” and can be customized.', 'siteguard' ); ?> |
| 349 | </div> |
| 350 | <hr /> |
| 351 | <?php |
| 352 | wp_nonce_field( 'siteguard-menu-rename-login-submit' ); |
| 353 | submit_button(); |
| 354 | ?> |
| 355 | </form> |
| 356 | </div> |
| 357 | <?php |
| 358 | } |
| 359 | } |
| 360 |