/** * Throws an exception if the current user triggers flood control. * * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException */ protected function contactFloodControl() { $limit = $this->config('contact.settings')->get('flood.limit'); $interval = $this->config('contact.settings')->get('flood.interval'); if (!$this->flood->isAllowed('contact', $limit, $interval)) { drupal_set_message($this->t('You cannot send more than %limit messages in @interval. Try again later.', array('%limit' => $limit, '@interval' => format_interval($interval))), 'error'); throw new AccessDeniedHttpException(); } }
/** * {@inheritdoc} */ public function authenticate(Request $request) { $flood_config = $this->configFactory->get('user.flood'); $username = $request->headers->get('PHP_AUTH_USER'); $password = $request->headers->get('PHP_AUTH_PW'); // Flood protection: this is very similar to the user login form code. // @see \Drupal\user\Form\UserLoginForm::validateAuthentication() // Do not allow any login from the current user's IP if the limit has been // reached. Default is 50 failed attempts allowed in one hour. This is // independent of the per-user limit to catch attempts from one IP to log // in to many different user accounts. We have a reasonably high limit // since there may be only one apparent IP for all users at an institution. if ($this->flood->isAllowed('basic_auth.failed_login_ip', $flood_config->get('ip_limit'), $flood_config->get('ip_window'))) { $accounts = $this->entityManager->getStorage('user')->loadByProperties(array('name' => $username, 'status' => 1)); $account = reset($accounts); if ($account) { if ($flood_config->get('uid_only')) { // Register flood events based on the uid only, so they apply for any // IP address. This is the most secure option. $identifier = $account->id(); } else { // The default identifier is a combination of uid and IP address. This // is less secure but more resistant to denial-of-service attacks that // could lock out all users with public user names. $identifier = $account->id() . '-' . $request->getClientIP(); } // Don't allow login if the limit for this user has been reached. // Default is to allow 5 failed attempts every 6 hours. if ($this->flood->isAllowed('basic_auth.failed_login_user', $flood_config->get('user_limit'), $flood_config->get('user_window'), $identifier)) { $uid = $this->userAuth->authenticate($username, $password); if ($uid) { $this->flood->clear('basic_auth.failed_login_user', $identifier); return $this->entityManager->getStorage('user')->load($uid); } else { // Register a per-user failed login event. $this->flood->register('basic_auth.failed_login_user', $flood_config->get('user_window'), $identifier); } } } } // Always register an IP-based failed login event. $this->flood->register('basic_auth.failed_login_ip', $flood_config->get('ip_window')); return []; }
/** * {@inheritdoc} */ public function validateForm(array &$form, FormStateInterface $form_state) { $message = parent::validateForm($form, $form_state); // Check if flood control has been activated for sending emails. if (!$this->currentUser()->hasPermission('administer contact forms') && (!$message->isPersonal() || !$this->currentUser()->hasPermission('administer users'))) { $limit = $this->config('contact.settings')->get('flood.limit'); $interval = $this->config('contact.settings')->get('flood.interval'); if (!$this->flood->isAllowed('contact', $limit, $interval)) { $form_state->setErrorByName('', $this->t('You cannot send more than %limit messages in @interval. Try again later.', array('%limit' => $limit, '@interval' => $this->dateFormatter->formatInterval($interval)))); } } return $message; }
/** * Enforces flood control for the current login request. * * @param \Symfony\Component\HttpFoundation\Request $request * The current request. * @param string $username * The user name sent for login credentials. */ protected function floodControl(Request $request, $username) { $flood_config = $this->config('user.flood'); if (!$this->flood->isAllowed('user.failed_login_ip', $flood_config->get('ip_limit'), $flood_config->get('ip_window'))) { throw new AccessDeniedHttpException('Access is blocked because of IP based flood prevention.', NULL, Response::HTTP_TOO_MANY_REQUESTS); } if ($identifier = $this->getLoginFloodIdentifier($request, $username)) { // Don't allow login if the limit for this user has been reached. // Default is to allow 5 failed attempts every 6 hours. if (!$this->flood->isAllowed('user.http_login', $flood_config->get('user_limit'), $flood_config->get('user_window'), $identifier)) { if ($flood_config->get('uid_only')) { $error_message = sprintf('There have been more than %s failed login attempts for this account. It is temporarily blocked. Try again later or request a new password.', $flood_config->get('user_limit')); } else { $error_message = 'Too many failed login attempts from your IP address. This IP address is temporarily blocked.'; } throw new AccessDeniedHttpException($error_message, NULL, Response::HTTP_TOO_MANY_REQUESTS); } } }
/** * Checks supplied username/password against local users table. * * If successful, $form_state->get('uid') is set to the matching user ID. */ public function validateAuthentication(array &$form, FormStateInterface $form_state) { $password = trim($form_state->getValue('pass')); $flood_config = $this->config('user.flood'); if (!$form_state->isValueEmpty('name') && !empty($password)) { // Do not allow any login from the current user's IP if the limit has been // reached. Default is 50 failed attempts allowed in one hour. This is // independent of the per-user limit to catch attempts from one IP to log // in to many different user accounts. We have a reasonably high limit // since there may be only one apparent IP for all users at an institution. if (!$this->flood->isAllowed('user.failed_login_ip', $flood_config->get('ip_limit'), $flood_config->get('ip_window'))) { $form_state->set('flood_control_triggered', 'ip'); return; } $accounts = $this->userStorage->loadByProperties(array('name' => $form_state->getValue('name'), 'status' => 1)); $account = reset($accounts); if ($account) { if ($flood_config->get('uid_only')) { // Register flood events based on the uid only, so they apply for any // IP address. This is the most secure option. $identifier = $account->id(); } else { // The default identifier is a combination of uid and IP address. This // is less secure but more resistant to denial-of-service attacks that // could lock out all users with public user names. $identifier = $account->id() . '-' . $this->getRequest()->getClientIP(); } $form_state->set('flood_control_user_identifier', $identifier); // Don't allow login if the limit for this user has been reached. // Default is to allow 5 failed attempts every 6 hours. if (!$this->flood->isAllowed('user.failed_login_user', $flood_config->get('user_limit'), $flood_config->get('user_window'), $identifier)) { $form_state->set('flood_control_triggered', 'user'); return; } } // We are not limited by flood control, so try to authenticate. // Store $uid in form state as a flag for self::validateFinal(). $uid = $this->userAuth->authenticate($form_state->getValue('name'), $password); $form_state->set('uid', $uid); } }