/** * handle the password reset code entry after sending an email with the code to the user * * The user has entered the code that was sent to him in order to reset * his or her password up to MAX_CODE_RETRIES-1 times. The code is * generated from the "user_activation_key" within the database table * "users" that is also used by wp_login.php. This way, we do not have * to alter the databese structure at all. * * If the code entered is valid, ths ueser will be taken to the password * reset mask. If the code is wrong more than MAX_CODE_RETRIES times, * the user_activation_key is set randomly to prevent brute force * attacks. Of course, the user can receive a new code via email, but * he or she has to request one manually again. * * @return WP_Error error message if code was wrong (too often) */ public static function handle_reset_code() { // Prevent Cross-Site-Request-Forgery if (!Handlers::is_nonce_ok('code_entry_form')) { return new \WP_Error('nonce', __('There seems to be a security issue. Please do not continue, but inform us!', 'YALW'), 'error'); } $MAX_CODE_RETRIES = 2; $user_login = Session::get_user_login(); $events = new \WP_Error(); // get the code from the user activation key stored in the database $db_code = Handlers::get_retrieval_code($user_login); if (empty($db_code)) { // could not get the code from the database Session::set_next_widget_task('check_code'); $events->add('unknown_error', __('There seems to be a problem with our database. Sorry. Please try again later.', 'YALW'), 'error'); } elseif ($_POST['YALW_code'] != $db_code) { Session::increment_code_error_count(); if (Session::get_code_error_count() > $MAX_CODE_RETRIES) { // maximum retries exceeded, set new code Handlers::set_random_reset_code($user_login); /* * We log the fact that the code was entered wrong too often. Too many of these * entries in the logfile, e. g. three within a certain period of time, can be used * via fail2ban to block the user's IP address. It is likely someone is trying to * "brute force" the plugin. */ \openlog('yalw(' . $_SERVER['HTTP_HOST'] . ')', LOG_NDELAY | LOG_PID, LOG_AUTH); \syslog(LOG_NOTICE, "Code reset failure for {$user_login} from " . Handlers::get_remote_address()); /* * From a security driven point of view, we could erase the * username in the session so the user must reenter it -- we * don't for the sake of usability. */ Session::clean_code_error_count(); Session::set_next_widget_task('retrieve_code'); $events->add('code_reset', __('The code was wrong too often. Please get a new one.', 'YALW'), 'warn'); } else { /* * We log the fact that the code was entered wrong. Too many of these * entries in the logfile, e. g. three within a certain period of time, can be used * via fail2ban to block the user's IP address. It is likely someone is trying to * "brute force" the plugin. */ \openlog('yalw(' . $_SERVER['HTTP_HOST'] . ')', LOG_NDELAY | LOG_PID, LOG_AUTHPRIV); \syslog(LOG_NOTICE, "Code entry failure for {$user_login} from " . Handlers::get_remote_address()); // code wrong Session::set_next_widget_task('check_code'); $events->add('code_mismatch', __('The code is wrong.', 'YALW'), 'warn'); } } else { // code is OK, remove dispensable stuff from session, and go to password entry Session::clean_code_error_count(); Session::set_next_widget_task('enter_new_password'); return null; } return $events; }