Append bruteForce Protection number of allowed failed Login parameter (#1506)
This commit is contained in:
parent
caa408b424
commit
aa45cf148a
|
@ -15,31 +15,32 @@ sub defaultValues {
|
||||||
'type' => 'category'
|
'type' => 'category'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'authChoiceParam' => 'lmAuth',
|
'authChoiceParam' => 'lmAuth',
|
||||||
'authentication' => 'Demo',
|
'authentication' => 'Demo',
|
||||||
'available2F' => 'UTOTP,TOTP,U2F,REST,Ext2F,Yubikey',
|
'available2F' => 'UTOTP,TOTP,U2F,REST,Ext2F,Yubikey',
|
||||||
'available2FSelfRegistration' => 'TOTP,U2F,Yubikey',
|
'available2FSelfRegistration' => 'TOTP,U2F,Yubikey',
|
||||||
'bruteForceProtectionMaxAge' => 300,
|
'bruteForceProtectionMaxAge' => 300,
|
||||||
'bruteForceProtectionTempo' => 30,
|
'bruteForceProtectionMaxFailed' => 3,
|
||||||
'captcha_mail_enabled' => 1,
|
'bruteForceProtectionTempo' => 30,
|
||||||
'captcha_register_enabled' => 1,
|
'captcha_mail_enabled' => 1,
|
||||||
'captcha_size' => 6,
|
'captcha_register_enabled' => 1,
|
||||||
'casAccessControlPolicy' => 'none',
|
'captcha_size' => 6,
|
||||||
'casAuthnLevel' => 1,
|
'casAccessControlPolicy' => 'none',
|
||||||
'checkTime' => 600,
|
'casAuthnLevel' => 1,
|
||||||
'checkXSS' => 1,
|
'checkTime' => 600,
|
||||||
'confirmFormMethod' => 'post',
|
'checkXSS' => 1,
|
||||||
'cookieName' => 'lemonldap',
|
'confirmFormMethod' => 'post',
|
||||||
'cspConnect' => '\'self\'',
|
'cookieName' => 'lemonldap',
|
||||||
'cspDefault' => '\'self\'',
|
'cspConnect' => '\'self\'',
|
||||||
'cspFont' => '\'self\'',
|
'cspDefault' => '\'self\'',
|
||||||
'cspFormAction' => '\'self\'',
|
'cspFont' => '\'self\'',
|
||||||
'cspImg' => '\'self\' data:',
|
'cspFormAction' => '\'self\'',
|
||||||
'cspScript' => '\'self\'',
|
'cspImg' => '\'self\' data:',
|
||||||
'cspStyle' => '\'self\'',
|
'cspScript' => '\'self\'',
|
||||||
'dbiAuthnLevel' => 2,
|
'cspStyle' => '\'self\'',
|
||||||
'dbiExportedVars' => {},
|
'dbiAuthnLevel' => 2,
|
||||||
'demoExportedVars' => {
|
'dbiExportedVars' => {},
|
||||||
|
'demoExportedVars' => {
|
||||||
'cn' => 'cn',
|
'cn' => 'cn',
|
||||||
'mail' => 'mail',
|
'mail' => 'mail',
|
||||||
'uid' => 'uid'
|
'uid' => 'uid'
|
||||||
|
|
|
@ -615,6 +615,10 @@ sub attributes {
|
||||||
'default' => 300,
|
'default' => 300,
|
||||||
'type' => 'int'
|
'type' => 'int'
|
||||||
},
|
},
|
||||||
|
'bruteForceProtectionMaxFailed' => {
|
||||||
|
'default' => 3,
|
||||||
|
'type' => 'int'
|
||||||
|
},
|
||||||
'bruteForceProtectionTempo' => {
|
'bruteForceProtectionTempo' => {
|
||||||
'default' => 30,
|
'default' => 30,
|
||||||
'type' => 'int'
|
'type' => 'int'
|
||||||
|
|
|
@ -612,7 +612,13 @@ sub attributes {
|
||||||
default => 300,
|
default => 300,
|
||||||
type => 'int',
|
type => 'int',
|
||||||
documentation =>
|
documentation =>
|
||||||
'Brute force attack protection -> Max age third failed login',
|
'Brute force attack protection -> Max age between last and first allowed failed login',
|
||||||
|
},
|
||||||
|
bruteForceProtectionMaxFailed => {
|
||||||
|
default => 3,
|
||||||
|
type => 'int',
|
||||||
|
documentation =>
|
||||||
|
'Brute force attack protection -> Max allowed failed login',
|
||||||
},
|
},
|
||||||
grantSessionRules => {
|
grantSessionRules => {
|
||||||
type => 'grantContainer',
|
type => 'grantContainer',
|
||||||
|
|
|
@ -27,7 +27,7 @@ sub init {
|
||||||
sub run {
|
sub run {
|
||||||
my ( $self, $req ) = @_;
|
my ( $self, $req ) = @_;
|
||||||
|
|
||||||
my $MaxAge = 0;
|
my $MaxAge = $self->conf->{bruteForceProtectionMaxAge} + 1;
|
||||||
my $countFailed = 0;
|
my $countFailed = 0;
|
||||||
my @lastFailedLoginEpoch = ();
|
my @lastFailedLoginEpoch = ();
|
||||||
|
|
||||||
|
@ -37,32 +37,39 @@ sub run {
|
||||||
}
|
}
|
||||||
|
|
||||||
$self->logger->debug(" Number of failedLogin = $countFailed");
|
$self->logger->debug(" Number of failedLogin = $countFailed");
|
||||||
return PE_OK if ( $countFailed < 3 );
|
return PE_OK
|
||||||
|
if ( $countFailed <= $self->conf->{bruteForceProtectionMaxFailed} );
|
||||||
|
|
||||||
foreach ( 0 .. 2 ) {
|
foreach ( 0 .. $self->conf->{bruteForceProtectionMaxFailed} - 1 ) {
|
||||||
if ( defined $req->sessionInfo->{_loginHistory}->{failedLogin}->[$_] ) {
|
if ( defined $req->sessionInfo->{_loginHistory}->{failedLogin}->[$_] )
|
||||||
|
{
|
||||||
push @lastFailedLoginEpoch,
|
push @lastFailedLoginEpoch,
|
||||||
$req->sessionInfo->{_loginHistory}->{failedLogin}->[$_]->{_utime};
|
$req->sessionInfo->{_loginHistory}->{failedLogin}->[$_]
|
||||||
|
->{_utime};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$self->logger->debug("BruteForceProtection enabled");
|
$self->logger->debug("BruteForceProtection enabled");
|
||||||
|
|
||||||
# If Auth_N-2 older than MaxAge -> another try allowed
|
# If Auth_N-MaxFailed older than MaxAge -> another try allowed
|
||||||
$MaxAge = $lastFailedLoginEpoch[0] - $lastFailedLoginEpoch[2];
|
$MaxAge
|
||||||
|
= $lastFailedLoginEpoch[0]
|
||||||
|
- $lastFailedLoginEpoch[ $self->conf->{bruteForceProtectionMaxFailed} - 1 ]
|
||||||
|
if $self->conf->{bruteForceProtectionMaxFailed};
|
||||||
$self->logger->debug(" -> MaxAge = $MaxAge");
|
$self->logger->debug(" -> MaxAge = $MaxAge");
|
||||||
return PE_OK
|
return PE_OK
|
||||||
if ( $MaxAge > $self->conf->{bruteForceProtectionMaxAge} );
|
if ( $MaxAge > $self->conf->{bruteForceProtectionMaxAge} );
|
||||||
|
|
||||||
# Delta between the two last failed logins -> Auth_N - Auth_N-1
|
# Delta between the two last failed logins -> Auth_N - Auth_N-1
|
||||||
my $delta = time - $lastFailedLoginEpoch[1];
|
my $delta = 0;
|
||||||
|
$delta = time - $lastFailedLoginEpoch[1]
|
||||||
|
if defined $lastFailedLoginEpoch[1];
|
||||||
$self->logger->debug(" -> Delta = $delta");
|
$self->logger->debug(" -> Delta = $delta");
|
||||||
|
|
||||||
# Delta between the two last failed logins < 30s => wait
|
# Delta between the two last failed logins < Tempo => wait
|
||||||
return PE_OK
|
return PE_OK
|
||||||
unless ( $delta <= $self->conf->{bruteForceProtectionTempo} );
|
unless ( $delta <= $self->conf->{bruteForceProtectionTempo} );
|
||||||
|
|
||||||
# Account locked
|
# Account locked
|
||||||
#shift @{ $req->sessionInfo->{_loginHistory}->{failedLogin} };
|
|
||||||
return PE_WAIT;
|
return PE_WAIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,9 @@ my $client = LLNG::Manager::Test->new(
|
||||||
loginHistoryEnabled => 1,
|
loginHistoryEnabled => 1,
|
||||||
bruteForceProtection => 1,
|
bruteForceProtection => 1,
|
||||||
bruteForceProtectionTempo => 5,
|
bruteForceProtectionTempo => 5,
|
||||||
|
bruteForceProtectionMaxFailed => 4,
|
||||||
|
failedLoginNumber => 6,
|
||||||
|
successLoginNumber => 4,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -30,7 +33,7 @@ ok(
|
||||||
length => 23,
|
length => 23,
|
||||||
accept => 'text/html',
|
accept => 'text/html',
|
||||||
),
|
),
|
||||||
'Auth query'
|
'1st Auth query'
|
||||||
);
|
);
|
||||||
count(1);
|
count(1);
|
||||||
my $id1 = expectCookie($res);
|
my $id1 = expectCookie($res);
|
||||||
|
@ -46,7 +49,55 @@ ok(
|
||||||
length => 23,
|
length => 23,
|
||||||
accept => 'text/html',
|
accept => 'text/html',
|
||||||
),
|
),
|
||||||
'Auth query'
|
'2nd Auth query'
|
||||||
|
);
|
||||||
|
count(1);
|
||||||
|
$id1 = expectCookie($res);
|
||||||
|
expectRedirection( $res, 'http://auth.example.com/' );
|
||||||
|
|
||||||
|
$client->logout($id1);
|
||||||
|
|
||||||
|
## Third successful connection
|
||||||
|
ok(
|
||||||
|
$res = $client->_post(
|
||||||
|
'/',
|
||||||
|
IO::String->new('user=dwho&password=dwho'),
|
||||||
|
length => 23,
|
||||||
|
accept => 'text/html',
|
||||||
|
),
|
||||||
|
'3rd Auth query'
|
||||||
|
);
|
||||||
|
count(1);
|
||||||
|
$id1 = expectCookie($res);
|
||||||
|
expectRedirection( $res, 'http://auth.example.com/' );
|
||||||
|
|
||||||
|
$client->logout($id1);
|
||||||
|
|
||||||
|
## Forth successful connection
|
||||||
|
ok(
|
||||||
|
$res = $client->_post(
|
||||||
|
'/',
|
||||||
|
IO::String->new('user=dwho&password=dwho'),
|
||||||
|
length => 23,
|
||||||
|
accept => 'text/html',
|
||||||
|
),
|
||||||
|
'4th Auth query'
|
||||||
|
);
|
||||||
|
count(1);
|
||||||
|
$id1 = expectCookie($res);
|
||||||
|
expectRedirection( $res, 'http://auth.example.com/' );
|
||||||
|
|
||||||
|
$client->logout($id1);
|
||||||
|
|
||||||
|
## Fifth successful connection
|
||||||
|
ok(
|
||||||
|
$res = $client->_post(
|
||||||
|
'/',
|
||||||
|
IO::String->new('user=dwho&password=dwho'),
|
||||||
|
length => 23,
|
||||||
|
accept => 'text/html',
|
||||||
|
),
|
||||||
|
'5th Auth query'
|
||||||
);
|
);
|
||||||
count(1);
|
count(1);
|
||||||
$id1 = expectCookie($res);
|
$id1 = expectCookie($res);
|
||||||
|
@ -61,7 +112,7 @@ ok(
|
||||||
IO::String->new('user=dwho&password=ohwd'),
|
IO::String->new('user=dwho&password=ohwd'),
|
||||||
length => 23
|
length => 23
|
||||||
),
|
),
|
||||||
'Auth query'
|
'1st Bad Auth query'
|
||||||
);
|
);
|
||||||
count(1);
|
count(1);
|
||||||
expectReject($res);
|
expectReject($res);
|
||||||
|
@ -73,12 +124,36 @@ ok(
|
||||||
IO::String->new('user=dwho&password=ohwd'),
|
IO::String->new('user=dwho&password=ohwd'),
|
||||||
length => 23
|
length => 23
|
||||||
),
|
),
|
||||||
'Auth query'
|
'2nd Bad Auth query'
|
||||||
);
|
);
|
||||||
count(1);
|
count(1);
|
||||||
expectReject($res);
|
expectReject($res);
|
||||||
|
|
||||||
## Third failed connection -> rejected
|
## Third failed connection
|
||||||
|
ok(
|
||||||
|
$res = $client->_post(
|
||||||
|
'/',
|
||||||
|
IO::String->new('user=dwho&password=ohwd'),
|
||||||
|
length => 23
|
||||||
|
),
|
||||||
|
'3rd Bad Auth query'
|
||||||
|
);
|
||||||
|
count(1);
|
||||||
|
expectReject($res);
|
||||||
|
|
||||||
|
## Forth failed connection
|
||||||
|
ok(
|
||||||
|
$res = $client->_post(
|
||||||
|
'/',
|
||||||
|
IO::String->new('user=dwho&password=ohwd'),
|
||||||
|
length => 23
|
||||||
|
),
|
||||||
|
'4th Bad Auth query'
|
||||||
|
);
|
||||||
|
count(1);
|
||||||
|
expectReject($res);
|
||||||
|
|
||||||
|
## Fifth failed connection -> rejected
|
||||||
ok(
|
ok(
|
||||||
$res = $client->_post(
|
$res = $client->_post(
|
||||||
'/',
|
'/',
|
||||||
|
@ -86,15 +161,15 @@ ok(
|
||||||
length => 23,
|
length => 23,
|
||||||
accept => 'text/html',
|
accept => 'text/html',
|
||||||
),
|
),
|
||||||
'Auth query'
|
'5th Bad Auth query'
|
||||||
);
|
);
|
||||||
count(1);
|
count(1);
|
||||||
|
|
||||||
ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/, 'Protection enabled' );
|
ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/, 'Rejected -> Protection enabled' );
|
||||||
count(1);
|
count(1);
|
||||||
sleep 1;
|
sleep 1;
|
||||||
|
|
||||||
## Fourth failed connection -> Rejected
|
## Sixth failed connection -> Rejected
|
||||||
ok(
|
ok(
|
||||||
$res = $client->_post(
|
$res = $client->_post(
|
||||||
'/',
|
'/',
|
||||||
|
@ -102,15 +177,15 @@ ok(
|
||||||
length => 23,
|
length => 23,
|
||||||
accept => 'text/html',
|
accept => 'text/html',
|
||||||
),
|
),
|
||||||
'Auth query'
|
'6th Bad Auth query'
|
||||||
);
|
);
|
||||||
count(1);
|
count(1);
|
||||||
|
|
||||||
ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/, 'Protection enabled' );
|
ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/, 'Rejected -> Protection enabled' );
|
||||||
count(1);
|
count(1);
|
||||||
sleep 2;
|
sleep 2;
|
||||||
|
|
||||||
## Third successful connection -> Rejected
|
## Sixth successful connection -> Rejected
|
||||||
ok(
|
ok(
|
||||||
$res = $client->_post(
|
$res = $client->_post(
|
||||||
'/',
|
'/',
|
||||||
|
@ -118,15 +193,15 @@ ok(
|
||||||
length => 23,
|
length => 23,
|
||||||
accept => 'text/html',
|
accept => 'text/html',
|
||||||
),
|
),
|
||||||
'Auth query'
|
'6th Auth query'
|
||||||
);
|
);
|
||||||
count(1);
|
count(1);
|
||||||
|
|
||||||
ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/, 'Protection enabled' );
|
ok( $res->[2]->[0] =~ /<span trmsg="86"><\/span>/, 'Rejected -> Protection enabled' );
|
||||||
count(1);
|
count(1);
|
||||||
sleep 3;
|
sleep 3;
|
||||||
|
|
||||||
## Fourth successful connection -> Accepted
|
## Seventh successful connection -> Accepted
|
||||||
ok(
|
ok(
|
||||||
$res = $client->_post(
|
$res = $client->_post(
|
||||||
'/',
|
'/',
|
||||||
|
@ -134,7 +209,7 @@ ok(
|
||||||
length => 37,
|
length => 37,
|
||||||
accept => 'text/html',
|
accept => 'text/html',
|
||||||
),
|
),
|
||||||
'Auth query'
|
'7th Auth query'
|
||||||
);
|
);
|
||||||
count(1);
|
count(1);
|
||||||
$id1 = expectCookie($res);
|
$id1 = expectCookie($res);
|
||||||
|
@ -145,9 +220,9 @@ ok( $res->[2]->[0] =~ /trspan="lastLogins"/, 'History found' )
|
||||||
my @c = ( $res->[2]->[0] =~ /<td>127.0.0.1/gs );
|
my @c = ( $res->[2]->[0] =~ /<td>127.0.0.1/gs );
|
||||||
my @cf = ( $res->[2]->[0] =~ /PE5<\/td>/gs );
|
my @cf = ( $res->[2]->[0] =~ /PE5<\/td>/gs );
|
||||||
|
|
||||||
# History with 5 entries
|
# History with 10 entries
|
||||||
ok( @c == 7, ' -> Seven entries found' );
|
ok( @c == 10, ' -> Ten entries found' );
|
||||||
ok( @cf == 4, " -> Four 'failedLogin' entries found" );
|
ok( @cf == 6, " -> Six 'failedLogin' entries found" );
|
||||||
count(3);
|
count(3);
|
||||||
|
|
||||||
$client->logout($id1);
|
$client->logout($id1);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user