mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
[SS-2017-005] User enumeration via timing attack mitigated
This commit is contained in:
parent
c0003bca3d
commit
f0262a8fd9
@ -18,9 +18,18 @@ abstract class LoginForm extends Form {
|
|||||||
* form.
|
* form.
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
|
|
||||||
protected $authenticator_class;
|
protected $authenticator_class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimum amount of time authenticating is allowed to take in milliseconds.
|
||||||
|
*
|
||||||
|
* Protects against timing enumeration attacks
|
||||||
|
*
|
||||||
|
* @config
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private static $min_auth_time = 350;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the authenticator instance
|
* Get the authenticator instance
|
||||||
*
|
*
|
||||||
|
@ -150,6 +150,10 @@ class MemberAuthenticator extends Authenticator {
|
|||||||
* @see Security::setDefaultAdmin()
|
* @see Security::setDefaultAdmin()
|
||||||
*/
|
*/
|
||||||
public static function authenticate($data, Form $form = null) {
|
public static function authenticate($data, Form $form = null) {
|
||||||
|
// minimum execution time for authenticating a member
|
||||||
|
$minExecTime = LoginForm::config()->min_auth_time / 1000;
|
||||||
|
$startTime = microtime(true);
|
||||||
|
|
||||||
// Find authenticated member
|
// Find authenticated member
|
||||||
$member = static::authenticate_member($data, $form, $success);
|
$member = static::authenticate_member($data, $form, $success);
|
||||||
|
|
||||||
@ -170,6 +174,11 @@ class MemberAuthenticator extends Authenticator {
|
|||||||
|
|
||||||
if($success) Session::clear('BackURL');
|
if($success) Session::clear('BackURL');
|
||||||
|
|
||||||
|
$waitFor = $minExecTime - (microtime(true) - $startTime);
|
||||||
|
if ($waitFor > 0) {
|
||||||
|
usleep($waitFor * 1000000);
|
||||||
|
}
|
||||||
|
|
||||||
return $success ? $member : null;
|
return $success ? $member : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,6 +294,10 @@ JS;
|
|||||||
* @param array $data Submitted data
|
* @param array $data Submitted data
|
||||||
*/
|
*/
|
||||||
public function forgotPassword($data) {
|
public function forgotPassword($data) {
|
||||||
|
// minimum execution time for authenticating a member
|
||||||
|
$minExecTime = self::config()->min_auth_time / 1000;
|
||||||
|
$startTime = microtime(true);
|
||||||
|
|
||||||
// Ensure password is given
|
// Ensure password is given
|
||||||
if(empty($data['Email'])) {
|
if(empty($data['Email'])) {
|
||||||
$this->sessionMessage(
|
$this->sessionMessage(
|
||||||
@ -311,10 +315,8 @@ JS;
|
|||||||
// Allow vetoing forgot password requests
|
// Allow vetoing forgot password requests
|
||||||
$results = $this->extend('forgotPassword', $member);
|
$results = $this->extend('forgotPassword', $member);
|
||||||
if($results && is_array($results) && in_array(false, $results, true)) {
|
if($results && is_array($results) && in_array(false, $results, true)) {
|
||||||
return $this->controller->redirect('Security/lostpassword');
|
$this->controller->redirect('Security/lostpassword');
|
||||||
}
|
} elseif ($member) {
|
||||||
|
|
||||||
if($member) {
|
|
||||||
$token = $member->generateAutologinTokenAndStoreHash();
|
$token = $member->generateAutologinTokenAndStoreHash();
|
||||||
|
|
||||||
$e = Member_ForgotPasswordEmail::create();
|
$e = Member_ForgotPasswordEmail::create();
|
||||||
@ -338,6 +340,10 @@ JS;
|
|||||||
|
|
||||||
$this->controller->redirect('Security/lostpassword');
|
$this->controller->redirect('Security/lostpassword');
|
||||||
}
|
}
|
||||||
|
$waitFor = $minExecTime - (microtime(true) - $startTime);
|
||||||
|
if ($waitFor > 0) {
|
||||||
|
usleep($waitFor * 1000000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user