PluginProbe
Patchstack – WordPress & Plugins Security / 2.2.11
Patchstack – WordPress & Plugins Security v2.2.11
2.3.7 trunk 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.17 2.1.18 2.1.19 2.1.2 2.1.20 2.1.21 2.1.22 2.1.23 2.1.24 2.1.25 2.1.3 2.1.4 2.1.5 2.1.6 All 49 releases
← All changes | includes/login.php +61 -34 2.1.32.2.11 View file →
@@ -22,21 +22,21 @@
22 22 if ( $this->get_option( 'patchstack_license_free', 0 ) == 1 ) {
23 23 return;
24 24 }
25 25
26 - add_action( 'login_init', array( $this, 'add_captcha' ) );
27 - add_action( 'login_init', array( $this, 'check_ipban' ) );
28 - add_action( 'login_init', array( $this, 'check_logonhours' ) );
29 - add_action( 'login_head', array( $this, 'add_captcha' ) );
30 - add_action( 'login_enqueue_scripts', array( $this, 'login_enqueue_scripts' ), 1 );
26 + add_action( 'login_init', [ $this, 'add_captcha' ] );
27 + add_action( 'login_init', [ $this, 'check_ipban' ] );
28 + add_action( 'login_init', [ $this, 'check_logonhours' ] );
29 + add_action( 'login_head', [ $this, 'add_captcha' ] );
30 + add_action( 'login_enqueue_scripts', [ $this, 'login_enqueue_scripts' ], 1 );
31 31
32 32 // 2FA related actions.
33 33 if ( $this->get_option( 'patchstack_login_2fa', 0 ) ) {
34 - add_action( 'login_form', array( $this, 'tfa_login_form' ) );
35 - add_action( 'authenticate', array( $this, 'tfa_authenticate' ), 30, 3 );
36 - add_action( 'profile_personal_options', array( $this, 'tfa_profile_personal_options' ) );
37 - add_action( 'personal_options_update', array( $this, 'tfa_personal_options_update' ) );
38 - add_action( 'admin_enqueue_scripts', array( $this, 'tfa_admin_enqueue_scripts' ) );
34 + add_action( 'login_form', [ $this, 'tfa_login_form' ] );
35 + add_action( 'authenticate', [ $this, 'tfa_authenticate' ], 30, 3 );
36 + add_action( 'profile_personal_options', [ $this, 'tfa_profile_personal_options' ] );
37 + add_action( 'personal_options_update', [ $this, 'tfa_personal_options_update' ] );
38 + add_action( 'admin_enqueue_scripts', [ $this, 'tfa_admin_enqueue_scripts' ] );
39 39 }
40 40 }
41 41
42 42 /**
@@ -73,9 +73,8 @@
73 73 }
74 74
75 75 // If we have a valid user object, check to see if the user has 2FA enabled.
76 76 $enabled = get_user_option( 'webarx_2fa_enabled', $user->ID );
77 - $secret = get_user_option( 'webarx_2fa_secretkey', $user->ID );
78 77 if ( empty( $enabled ) ) {
79 78 return $user;
80 79 }
81 80
@@ -80,15 +79,16 @@
80 79 }
81 80
82 81 // If enabled, check to see if the verification code is being sent.
83 82 if ( ! isset( $_POST['patchstack_2fa'] ) || ( isset( $_POST['patchstack_2fa'] ) && $_POST['patchstack_2fa'] == '' ) ) {
84 - return new WP_Error( 'patchstack_2fa_empty_code', __( 'Please enter the 2FA authentication code that is generated on your device.', 'patchstack' ) );
83 + return new WP_Error( 'patchstack_2fa_empty_code', esc_attr__( 'Please enter the 2FA authentication code that is generated on your device.', 'patchstack' ) );
85 84 }
86 85
87 86 // Verify the code.
88 87 require_once dirname( __FILE__ ) . '/2fa/rfc6238.php';
88 + $secret = $this->tfa_get_secret( $user );
89 89 if ( ! TokenAuth6238::verify( $secret, trim( $_POST['patchstack_2fa'] ) ) ) {
90 - return new WP_Error( 'patchstack_2fa_invalid_code', __( 'The 2FA authentication code you entered is invalid.', 'patchstack' ) );
90 + return new WP_Error( 'patchstack_2fa_invalid_code', esc_attr__( 'The 2FA authentication code you entered is invalid.', 'patchstack' ) );
91 91 }
92 92
93 93 return $user;
94 94 }
@@ -99,17 +99,9 @@
99 99 * @param object $user
100 100 * @return void
101 101 */
102 102 public function tfa_profile_personal_options( $user ) {
103 - $secret = get_user_option( 'webarx_2fa_secretkey', $user->ID );
104 -
105 - // If user has no secret key set yet, generate one.
106 - if ( empty( $secret ) ) {
107 - require_once dirname( __FILE__ ) . '/2fa/rfc6238.php';
108 - $secret = TokenAuth6238::generateRandomClue();
109 - update_user_option( $user->ID, 'webarx_2fa_secretkey', $secret, true );
110 - }
111 -
103 + $secret = $this->tfa_get_secret( $user );
112 104 require_once dirname( __FILE__ ) . '/views/2fa-profile-configuration.php';
113 105 }
114 106
115 107 /**
@@ -127,13 +119,39 @@
127 119 *
128 120 * @return void
129 121 */
130 122 public function tfa_admin_enqueue_scripts() {
131 - wp_register_script( 'patchstack_qrcode', $this->plugin->url . '/assets/js/qrcode.min.js', array(), $this->plugin->version );
123 + wp_register_script( 'patchstack_qrcode', $this->plugin->url . '/assets/js/qrcode.min.js', [], $this->plugin->version );
132 124 wp_enqueue_script( 'patchstack_qrcode' );
133 125 }
134 126
135 127 /**
128 + * In case of legacy conditions, we encrypt the secret key and then store it.
129 + *
130 + * @return string
131 + */
132 + private function tfa_get_secret( $user ) {
133 + $secret = get_user_option( 'webarx_2fa_secretkey', $user->ID );
134 +
135 + // If user has no secret key set yet, generate one.
136 + if ( empty( $secret ) || strlen( $secret ) === 16 ) {
137 + if ( empty( $secret ) ) {
138 + require_once dirname( __FILE__ ) . '/2fa/rfc6238.php';
139 + $secret = TokenAuth6238::generateRandomClue();
140 + }
141 +
142 + $enc = $this->encrypt( $secret );
143 + update_user_option( $user->ID, 'webarx_2fa_secretkey', $enc['cipher'], true );
144 + update_user_option( $user->ID, 'webarx_2fa_secretkey_nonce', $enc['nonce'], true );
145 + } else {
146 + $nonce = get_user_option( 'webarx_2fa_secretkey_nonce', $user->ID );
147 + $secret = $this->decrypt( $secret, $nonce );
148 + }
149 +
150 + return $secret;
151 + }
152 +
153 + /**
136 154 * Check if the IP address is banned from attempting to guess passwords.
137 155 *
138 156 * @return void
139 157 */
@@ -147,12 +165,21 @@
147 165 if ( $this->plugin->ban->is_ip_whitelisted( $ip ) ) {
148 166 return;
149 167 }
150 168
169 + // Calculate block time.
170 + $minutes = (int) $this->get_option( 'patchstack_anti_bruteforce_minutes', 30 );
171 + $timeout = (int) $this->get_option( 'patchstack_anti_bruteforce_blocktime', 60 );
172 + if ( empty( $minutes ) || empty( $timeout ) ) {
173 + $time = 30 + 60;
174 + } else {
175 + $time = $minutes + $timeout;
176 + }
177 +
151 178 // Check if X failed login attempts were made.
152 179 global $wpdb;
153 180 $results = $wpdb->get_results(
154 - $wpdb->prepare( 'SELECT COUNT(*) AS numIps FROM ' . $wpdb->prefix . "patchstack_event_log WHERE ip = '%s' AND action = 'failed login' AND date >= ('" . current_time( 'mysql' ) . "' - INTERVAL %d MINUTE)", array( $ip, ( $this->get_option( 'patchstack_anti_bruteforce_blocktime', 60 ) + $this->get_option( 'patchstack_anti_bruteforce_minutes', 5 ) ) ) ),
181 + $wpdb->prepare( 'SELECT COUNT(*) AS numIps FROM ' . $wpdb->prefix . "patchstack_event_log WHERE ip = '%s' AND action = 'failed login' AND date >= ('" . current_time( 'mysql' ) . "' - INTERVAL %d MINUTE)", [ $ip, $time ] ),
155 182 OBJECT
156 183 );
157 184
158 185 // Determine the number of attempts.
@@ -163,9 +190,9 @@
163 190 }
164 191
165 192 // Block the user?
166 193 if ( $num >= $this->get_option( 'patchstack_anti_bruteforce_attempts', 10 ) ) {
167 - $this->plugin->firewall_base->display_error_page( 22 );
194 + $this->plugin->firewall_base->display_error_page( 24 );
168 195 }
169 196 }
170 197
171 198 /**
@@ -218,9 +245,9 @@
218 245 }
219 246
220 247 // Block the user?
221 248 if ( $block ) {
222 - wp_die( __( 'Access to the login page has been restricted due to set logon hours.', 'patchstack' ), __( 'Login Disallowed', 'patchstack' ) );
249 + wp_die( esc_attr__( 'Access to the login page has been restricted due to set logon hours.', 'patchstack' ), esc_attr__( 'Login Disallowed', 'patchstack' ) );
223 250 }
224 251 }
225 252
226 253 /**
@@ -253,22 +280,22 @@
253 280 }
254 281
255 282 // reCAPTCHA on the login page.
256 283 if ( $this->get_option( 'patchstack_captcha_login_form' ) ) {
257 - add_filter( 'login_form', array( $this->plugin->hardening, 'captcha_display' ) );
258 - add_filter( 'wp_authenticate_user', array( $this, 'login_captcha_check' ), 10, 2 );
284 + add_filter( 'login_form', [ $this->plugin->hardening, 'captcha_display' ] );
285 + add_filter( 'wp_authenticate_user', [ $this, 'login_captcha_check' ], 10, 2 );
259 286 }
260 287
261 288 // reCAPTCHA on the registration form.
262 289 if ( $this->get_option( 'patchstack_captcha_registration_form' ) ) {
263 - add_action( 'register_form', array( $this->plugin->hardening, 'captcha_display' ) );
264 - add_action( 'registration_errors', array( $this, 'general_captcha_check' ) );
290 + add_action( 'register_form', [ $this->plugin->hardening, 'captcha_display' ] );
291 + add_action( 'registration_errors', [ $this, 'general_captcha_check' ] );
265 292 }
266 293
267 294 // reCAPTCHA on the reset password form.
268 295 if ( $this->get_option( 'patchstack_captcha_reset_pwd_form' ) ) {
269 - add_action( 'lostpassword_form', array( $this->plugin->hardening, 'captcha_display' ) );
270 - add_action( 'allow_password_reset', array( $this, 'general_captcha_check' ) );
296 + add_action( 'lostpassword_form', [ $this->plugin->hardening, 'captcha_display' ] );
297 + add_action( 'allow_password_reset', [ $this, 'general_captcha_check' ] );
271 298 }
272 299 }
273 300
274 301 /**
@@ -284,9 +311,9 @@
284 311 if ( ! $result['response'] ) {
285 312 if ( $result['reason'] === 'ERROR_NO_KEYS' ) {
286 313 return $user;
287 314 }
288 - $error_message = sprintf( '<strong>%s</strong>: %s', 'Error', __( 'You have entered an incorrect reCAPTCHA value.', 'patchstack' ) );
315 + $error_message = sprintf( '<strong>%s</strong>: %s', 'Error', esc_attr__( 'You have entered an incorrect reCAPTCHA value.', 'patchstack' ) );
289 316
290 317 if ( $result['reason'] === 'VERIFICATION_FAILED' || $result['reason'] === 'RECAPTCHA_EMPTY_RESPONSE' ) {
291 318 wp_clear_auth_cookie();
292 319 return new WP_Error( 'patchstack_error', $error_message );
@@ -316,8 +343,8 @@
316 343 if ( ! is_wp_error( $error ) ) {
317 344 $error = new WP_Error();
318 345 }
319 346
320 - $error->add( 'patchstack_error', 'ERROR' . ':&nbsp;' . __( 'You have entered an incorrect reCAPTCHA value. Refresh this page and try again.', 'patchstack' ) );
347 + $error->add( 'patchstack_error', 'ERROR' . ':&nbsp;' . esc_attr__( 'You have entered an incorrect reCAPTCHA value. Refresh this page and try again.', 'patchstack' ) );
321 348 return $error;
322 349 }
323 350 }