diff --git a/src/applications/auth/provider/PhabricatorPasswordAuthProvider.php b/src/applications/auth/provider/PhabricatorPasswordAuthProvider.php --- a/src/applications/auth/provider/PhabricatorPasswordAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorPasswordAuthProvider.php @@ -255,18 +255,48 @@ $viewer = $request->getUser(); $content_source = PhabricatorContentSource::newFromRequest($request); + $captcha_limit = 5; + $hard_limit = 32; + $limit_window = phutil_units('15 minutes in seconds'); + + $failed_attempts = PhabricatorUserLog::loadRecentEventsFromThisIP( + PhabricatorUserLog::ACTION_LOGIN_FAILURE, + $limit_window); + + // If the same remote address has submitted several failed login attempts + // recently, require they provide a CAPTCHA response for new attempts. $require_captcha = false; $captcha_valid = false; if (AphrontFormRecaptchaControl::isRecaptchaEnabled()) { - $failed_attempts = PhabricatorUserLog::loadRecentEventsFromThisIP( - PhabricatorUserLog::ACTION_LOGIN_FAILURE, - 60 * 15); - if (count($failed_attempts) > 5) { + if (count($failed_attempts) > $captcha_limit) { $require_captcha = true; $captcha_valid = AphrontFormRecaptchaControl::processCaptcha($request); } } + // If the user has submitted quite a few failed login attempts recently, + // give them a hard limit. + if (count($failed_attempts) > $hard_limit) { + $guidance = array(); + + $guidance[] = pht( + 'Your remote address has failed too many login attempts recently. '. + 'Wait a few minutes before trying again.'); + + $guidance[] = pht( + 'If you are unable to log in to your account, you can '. + '[[ /login/email | send a reset link to your email address ]].'); + + $guidance = implode("\n\n", $guidance); + + $dialog = $controller->newDialog() + ->setTitle(pht('Too Many Login Attempts')) + ->appendChild(new PHUIRemarkupView($viewer, $guidance)) + ->addCancelButton('/auth/start/', pht('Wait Patiently')); + + return array(null, $dialog); + } + $response = null; $account = null; $log_user = null;