1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-10 08:52:39 +01:00

Replace old rate limiting in password login flow with "SystemAction" rate limiting

Summary:
Depends on D20667. Ref T13343. Password auth currently uses an older rate limiting mechanism, upgrade it to the modern "SystemAction" mechanism.

This mostly just improves consistency, although there are some tangential/theoretical benefits:

  - it's not obvious that making the user log GC very quickly could disable rate limiting;
  - if we let you configure action limits in the future, which we might, this would become configurable for free.

Test Plan:
  - With CAPTCHAs off, made a bunch of invalid login attempts. Got rate limited.
  - With CAPTCHAs on, made a bunch of invalid login attempts. Got downgraded to CAPTCHAs after a few.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13343

Differential Revision: https://secure.phabricator.com/D20668
This commit is contained in:
epriestley 2019-07-19 10:11:55 -07:00
parent e090b32c75
commit a75766c0e5
4 changed files with 53 additions and 30 deletions

View file

@ -2431,6 +2431,8 @@ phutil_register_library_map(array(
'PhabricatorAuthTestSMSAction' => 'applications/auth/action/PhabricatorAuthTestSMSAction.php',
'PhabricatorAuthTryEmailLoginAction' => 'applications/auth/action/PhabricatorAuthTryEmailLoginAction.php',
'PhabricatorAuthTryFactorAction' => 'applications/auth/action/PhabricatorAuthTryFactorAction.php',
'PhabricatorAuthTryPasswordAction' => 'applications/auth/action/PhabricatorAuthTryPasswordAction.php',
'PhabricatorAuthTryPasswordWithoutCAPTCHAAction' => 'applications/auth/action/PhabricatorAuthTryPasswordWithoutCAPTCHAAction.php',
'PhabricatorAuthUnlinkController' => 'applications/auth/controller/PhabricatorAuthUnlinkController.php',
'PhabricatorAuthValidateController' => 'applications/auth/controller/PhabricatorAuthValidateController.php',
'PhabricatorAuthWaitForApprovalMessageType' => 'applications/auth/message/PhabricatorAuthWaitForApprovalMessageType.php',
@ -8427,6 +8429,8 @@ phutil_register_library_map(array(
'PhabricatorAuthTestSMSAction' => 'PhabricatorSystemAction',
'PhabricatorAuthTryEmailLoginAction' => 'PhabricatorSystemAction',
'PhabricatorAuthTryFactorAction' => 'PhabricatorSystemAction',
'PhabricatorAuthTryPasswordAction' => 'PhabricatorSystemAction',
'PhabricatorAuthTryPasswordWithoutCAPTCHAAction' => 'PhabricatorSystemAction',
'PhabricatorAuthUnlinkController' => 'PhabricatorAuthController',
'PhabricatorAuthValidateController' => 'PhabricatorAuthController',
'PhabricatorAuthWaitForApprovalMessageType' => 'PhabricatorAuthMessageType',

View file

@ -0,0 +1,22 @@
<?php
final class PhabricatorAuthTryPasswordAction
extends PhabricatorSystemAction {
const TYPECONST = 'auth.password';
public function getActionConstant() {
return self::TYPECONST;
}
public function getScoreThreshold() {
return 100 / phutil_units('1 hour in seconds');
}
public function getLimitExplanation() {
return pht(
'Your remote address has made too many login attempts in a short '.
'period of time.');
}
}

View file

@ -0,0 +1,16 @@
<?php
final class PhabricatorAuthTryPasswordWithoutCAPTCHAAction
extends PhabricatorSystemAction {
const TYPECONST = 'auth.password-without-captcha';
public function getActionConstant() {
return self::TYPECONST;
}
public function getScoreThreshold() {
return 10 / phutil_units('1 hour in seconds');
}
}

View file

@ -255,48 +255,29 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider {
$viewer = $request->getUser();
$content_source = PhabricatorContentSource::newFromRequest($request);
$captcha_limit = 5;
$hard_limit = 32;
$limit_window = phutil_units('15 minutes in seconds');
$rate_actor = PhabricatorSystemActionEngine::newActorFromRequest($request);
$failed_attempts = PhabricatorUserLog::loadRecentEventsFromThisIP(
PhabricatorUserLog::ACTION_LOGIN_FAILURE,
$limit_window);
PhabricatorSystemActionEngine::willTakeAction(
array($rate_actor),
new PhabricatorAuthTryPasswordAction(),
1);
// 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()) {
if (count($failed_attempts) > $captcha_limit) {
try {
PhabricatorSystemActionEngine::willTakeAction(
array($rate_actor),
new PhabricatorAuthTryPasswordWithoutCAPTCHAAction(),
1);
} catch (PhabricatorSystemActionRateLimitException $ex) {
$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;