Example #1
0
 /**
  * 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;
 }