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 +52 -34 2.1.212.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 */
@@ -159,9 +177,9 @@
159 177
160 178 // Check if X failed login attempts were made.
161 179 global $wpdb;
162 180 $results = $wpdb->get_results(
163 - $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, $time ) ),
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 ] ),
164 182 OBJECT
165 183 );
166 184
167 185 // Determine the number of attempts.
@@ -172,9 +190,9 @@
172 190 }
173 191
174 192 // Block the user?
175 193 if ( $num >= $this->get_option( 'patchstack_anti_bruteforce_attempts', 10 ) ) {
176 - $this->plugin->firewall_base->display_error_page( 22 );
194 + $this->plugin->firewall_base->display_error_page( 24 );
177 195 }
178 196 }
179 197
180 198 /**
@@ -227,9 +245,9 @@
227 245 }
228 246
229 247 // Block the user?
230 248 if ( $block ) {
231 - 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' ) );
232 250 }
233 251 }
234 252
235 253 /**
@@ -262,22 +280,22 @@
262 280 }
263 281
264 282 // reCAPTCHA on the login page.
265 283 if ( $this->get_option( 'patchstack_captcha_login_form' ) ) {
266 - add_filter( 'login_form', array( $this->plugin->hardening, 'captcha_display' ) );
267 - 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 );
268 286 }
269 287
270 288 // reCAPTCHA on the registration form.
271 289 if ( $this->get_option( 'patchstack_captcha_registration_form' ) ) {
272 - add_action( 'register_form', array( $this->plugin->hardening, 'captcha_display' ) );
273 - 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' ] );
274 292 }
275 293
276 294 // reCAPTCHA on the reset password form.
277 295 if ( $this->get_option( 'patchstack_captcha_reset_pwd_form' ) ) {
278 - add_action( 'lostpassword_form', array( $this->plugin->hardening, 'captcha_display' ) );
279 - 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' ] );
280 298 }
281 299 }
282 300
283 301 /**
@@ -293,9 +311,9 @@
293 311 if ( ! $result['response'] ) {
294 312 if ( $result['reason'] === 'ERROR_NO_KEYS' ) {
295 313 return $user;
296 314 }
297 - $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' ) );
298 316
299 317 if ( $result['reason'] === 'VERIFICATION_FAILED' || $result['reason'] === 'RECAPTCHA_EMPTY_RESPONSE' ) {
300 318 wp_clear_auth_cookie();
301 319 return new WP_Error( 'patchstack_error', $error_message );
@@ -325,8 +343,8 @@
325 343 if ( ! is_wp_error( $error ) ) {
326 344 $error = new WP_Error();
327 345 }
328 346
329 - $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' ) );
330 348 return $error;
331 349 }
332 350 }