PluginProbe
Patchstack – WordPress & Plugins Security / 2.2.9
Patchstack – WordPress & Plugins Security v2.2.9
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 +47 -29 2.1.212.2.9 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
@@ -85,8 +84,9 @@
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 90 return new WP_Error( 'patchstack_2fa_invalid_code', __( 'The 2FA authentication code you entered is invalid.', 'patchstack' ) );
91 91 }
92 92
@@ -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 /**
@@ -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 /**