siteguard-admin-filter.php
1 month ago
siteguard-base.php
1 month ago
siteguard-captcha.php
1 month ago
siteguard-config.php
11 months ago
siteguard-disable-author-query.php
2 weeks ago
siteguard-disable-pingback.php
1 month ago
siteguard-disable-xmlrpc.php
1 month ago
siteguard-htaccess.php
2 weeks ago
siteguard-login-alert.php
1 month ago
siteguard-login-history.php
1 month ago
siteguard-login-lock.php
1 month ago
siteguard-rename-login.php
1 week ago
siteguard-updates-notify.php
1 month ago
siteguard-waf-exclude-rule.php
1 month ago
siteguard-login-lock.php
152 lines
| 1 | <?php |
| 2 | |
| 3 | class SiteGuard_LoginLock extends SiteGuard_Base { |
| 4 | const SITEGUARD_FAIL_ONCE_ERROR_CODE = 'siteguard-fail-once'; |
| 5 | protected $status = SITEGUARD_LOGIN_FAILED; |
| 6 | function __construct() { |
| 7 | global $siteguard_config; |
| 8 | |
| 9 | if ( '1' === $siteguard_config->get( 'loginlock_enable' ) ) { |
| 10 | add_action( 'wp_login_failed', array( $this, 'handler_wp_login_failed' ) ); |
| 11 | add_filter( 'authenticate', array( $this, 'handler_authenticate' ), 20, 3 ); |
| 12 | add_filter( 'xmlrpc_login_error', array( $this, 'handler_xmlrpc_login_error' ), 10, 2 ); |
| 13 | } |
| 14 | if ( '1' == $siteguard_config->get( 'loginlock_fail_once' ) ) { |
| 15 | add_filter( 'wp_authenticate_user', array( $this, 'handler_wp_authenticate_user' ), 99, 2 ); |
| 16 | } |
| 17 | } |
| 18 | function init() { |
| 19 | global $siteguard_config; |
| 20 | if ( true === siteguard_check_multisite() ) { |
| 21 | $siteguard_config->set( 'loginlock_enable', '1' ); |
| 22 | } else { |
| 23 | $siteguard_config->set( 'loginlock_enable', '0' ); |
| 24 | } |
| 25 | $siteguard_config->set( 'loginlock_interval', '5' ); |
| 26 | $siteguard_config->set( 'loginlock_threshold', '3' ); |
| 27 | $siteguard_config->set( 'loginlock_locksec', '60' ); |
| 28 | $siteguard_config->set( 'loginlock_fail_once', '0' ); |
| 29 | $siteguard_config->set( 'fail_once_admin_only', '1' ); |
| 30 | $siteguard_config->update(); |
| 31 | } |
| 32 | function get_status() { |
| 33 | return $this->status; |
| 34 | } |
| 35 | function handler_wp_login_failed( $username ) { |
| 36 | global $wpdb, $siteguard_config, $siteguard_login_history; |
| 37 | $table_name = $wpdb->prefix . SITEGUARD_TABLE_LOGIN; |
| 38 | |
| 39 | $ip_address = $this->get_ip(); |
| 40 | |
| 41 | $wpdb->query( 'START TRANSACTION' ); |
| 42 | $wpdb->query( $wpdb->prepare( "DELETE FROM $table_name WHERE status <> %d AND last_login_time < SYSDATE() - INTERVAL 1 HOUR;", SITEGUARD_LOGIN_SUCCESS ) ); |
| 43 | $result = $wpdb->get_row( $wpdb->prepare( "SELECT status, count, last_login_time from $table_name WHERE ip_address = %s", $ip_address ) ); |
| 44 | $data = array( |
| 45 | 'ip_address' => $ip_address, |
| 46 | 'status' => SITEGUARD_LOGIN_FAILED, |
| 47 | 'count' => 1, |
| 48 | 'last_login_time' => 0, |
| 49 | ); |
| 50 | $now_str = current_time( 'mysql' ); |
| 51 | $now_bin = strtotime( $now_str ); |
| 52 | if ( null === $result ) { |
| 53 | $data['last_login_time'] = $now_str; |
| 54 | $wpdb->insert( $table_name, $data ); |
| 55 | } else { |
| 56 | $data['last_login_time'] = $result->last_login_time; |
| 57 | $interval = intval( $siteguard_config->get( 'loginlock_interval' ) ); |
| 58 | $limit = strtotime( $result->last_login_time ) + $interval; |
| 59 | if ( SITEGUARD_LOGIN_SUCCESS == $result->status ) { |
| 60 | $data['last_login_time'] = $now_str; |
| 61 | $wpdb->update( $table_name, $data, array( 'ip_address' => $ip_address ) ); |
| 62 | } elseif ( SITEGUARD_LOGIN_FAILED == $result->status ) { |
| 63 | if ( $now_bin <= $limit ) { |
| 64 | $data['count'] = $result->count + 1; |
| 65 | } else { |
| 66 | $data['count'] = 1; |
| 67 | $data['last_login_time'] = $now_str; |
| 68 | } |
| 69 | if ( $data['count'] >= intval( $siteguard_config->get( 'loginlock_threshold' ) ) ) { |
| 70 | $data['status'] = SITEGUARD_LOGIN_LOCKED; |
| 71 | $data['last_login_time'] = $now_str; |
| 72 | $this->status = SITEGUARD_LOGIN_LOCKED; |
| 73 | } |
| 74 | $wpdb->update( $table_name, $data, array( 'ip_address' => $ip_address ) ); |
| 75 | } elseif ( SITEGUARD_LOGIN_FAIL_ONCE == $result->status || ( SITEGUARD_LOGIN_LOCKED == $result->status && $now_bin > strtotime( $result->last_login_time ) + intval( $siteguard_config->get( 'loginlock_locksec' ) ) ) ) { |
| 76 | $data['status'] = SITEGUARD_LOGIN_FAILED; |
| 77 | $data['count'] = 1; |
| 78 | $data['last_login_time'] = $now_str; |
| 79 | $wpdb->update( $table_name, $data, array( 'ip_address' => $ip_address ) ); |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | $wpdb->query( 'COMMIT' ); |
| 84 | |
| 85 | return; |
| 86 | } |
| 87 | function is_locked( $ip_address ) { |
| 88 | global $wpdb, $siteguard_config; |
| 89 | |
| 90 | $now_bin = strtotime( current_time( 'mysql' ) ); |
| 91 | $table_name = $wpdb->prefix . SITEGUARD_TABLE_LOGIN; |
| 92 | $result = $wpdb->get_row( $wpdb->prepare( "SELECT status, last_login_time from $table_name WHERE ip_address = %s", $ip_address ) ); |
| 93 | if ( null !== $result ) { |
| 94 | if ( SITEGUARD_LOGIN_LOCKED == $result->status && $now_bin <= strtotime( $result->last_login_time ) + intval( $siteguard_config->get( 'loginlock_locksec' ) ) ) { |
| 95 | return true; |
| 96 | } |
| 97 | } |
| 98 | return false; |
| 99 | } |
| 100 | function handler_authenticate( $user, $username, $password ) { |
| 101 | if ( $this->is_locked( $this->get_ip() ) ) { |
| 102 | $new_errors = new WP_Error(); |
| 103 | $new_errors->add( 'siteguard-error', esc_html__( 'ERROR: LOGIN LOCKED', 'siteguard' ) ); |
| 104 | $this->status = SITEGUARD_LOGIN_LOCKED; |
| 105 | return $new_errors; |
| 106 | } |
| 107 | return $user; |
| 108 | } |
| 109 | function handler_xmlrpc_login_error( $error, $user ) { |
| 110 | if ( is_wp_error( $user ) && 'siteguard-error' === $user->get_error_code() ) { |
| 111 | return new IXR_Error( 403, $user->get_error_message( 'siteguard-error' ) ); |
| 112 | } |
| 113 | return $error; |
| 114 | } |
| 115 | function handler_login_shake( $shake_error_codes ) { |
| 116 | $shake_error_codes[] = self::SITEGUARD_FAIL_ONCE_ERROR_CODE; |
| 117 | return $shake_error_codes; |
| 118 | } |
| 119 | function handler_wp_authenticate_user( $user, $password ) { |
| 120 | global $siteguard_login_history, $siteguard_config; |
| 121 | |
| 122 | if ( basename( $_SERVER['SCRIPT_NAME'] ) == 'xmlrpc.php' ) { |
| 123 | return $user; |
| 124 | } |
| 125 | |
| 126 | // Login failed |
| 127 | if ( is_wp_error( $user ) ) { |
| 128 | return $user; |
| 129 | } |
| 130 | if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) { |
| 131 | return $user; |
| 132 | } |
| 133 | if ( '1' == $siteguard_config->get( 'fail_once_admin_only' ) ) { |
| 134 | if ( ! $user->has_cap( 'administrator' ) ) { |
| 135 | return $user; |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | $user_login = $user->user_login; |
| 140 | |
| 141 | if ( ! $siteguard_login_history->is_exist( $user_login, SITEGUARD_LOGIN_FAIL_ONCE, 5/* secs after */, 60/* secs less */ ) ) { |
| 142 | $this->status = SITEGUARD_LOGIN_FAIL_ONCE; |
| 143 | |
| 144 | $new_error = new WP_Error(); |
| 145 | $new_error->add( self::SITEGUARD_FAIL_ONCE_ERROR_CODE, esc_html__( 'ERROR: Please log in again', 'siteguard' ) ); |
| 146 | add_filter( 'shake_error_codes', array( $this, 'handler_login_shake' ) ); |
| 147 | return $new_error; |
| 148 | } |
| 149 | return $user; |
| 150 | } |
| 151 | } |
| 152 |