Merge branch '2276' into 'v2.0'

2276

See merge request lemonldap-ng/lemonldap-ng!159
This commit is contained in:
Christophe Maudoux 2020-08-28 15:06:01 +02:00
commit 5d056699c4
21 changed files with 141 additions and 85 deletions

View File

@ -20,6 +20,15 @@ Go in Manager, ``General Parameters`` » ``Advanced Parameters`` »
``Security`` » ``Brute-force attack protection`` » ``Activation``\ and ``Security`` » ``Brute-force attack protection`` » ``Activation``\ and
set to ``On``. set to ``On``.
- **Parameters**:
- **Activation**: Enable/disable brute force attack protection
- **Lock time**: Waiting time before another login attempt
- **Allowed failed login**: Number of failed login attempts allowed before account is locked
- **Incremental lock**: Enable/disable incremental lock times
- **Incremental lock times**: List of comma separated lock time values in seconds
Incremental lock time enabled Incremental lock time enabled
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -35,33 +44,29 @@ in ``lemonldap-ng.ini`` [portal] section:
[portal] [portal]
bruteForceProtectionIncrementalTempo = 1 bruteForceProtectionIncrementalTempo = 1
Lock time increases between each failed login attempt. To modify lock Lock time increases between each failed login attempt after allowed failed logins.
time values ('5 15 60 300 600' seconds by default) or max lock time
value (900 seconds by default) edit ``lemonldap-ng.ini`` in [portal]
section:
.. code-block:: ini .. code-block:: ini
[portal] [portal]
bruteForceProtectionLockTimes = '5 15 60 300 600' bruteForceProtectionLockTimes = 5, 15, 60, 300, 600
bruteForceProtectionMaxLockTime = 900 bruteForceProtectionMaxLockTime = 900
.. note:: .. note::
Max lock time value is used by this plugin if a lock time is Max lock time value is used if a lock time is missing
missing (number of failed logins higher than listed lock time values). (number of failed logins higher than listed lock time values).
Lock time values can not be higher than max lock time. Lock time values can not be higher than max lock time.
Incremental lock time disabled Incremental lock time disabled
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
After ``bruteForceProtectionMaxFailed`` failed login attempts, user must After allowed failed login attempts, user must
wait ``bruteForceProtectionTempo`` seconds before trying to log in wait the lock time before trying to log in again.
again. To modify waiting time (30 seconds by default), MaxAge between To modify delta (MaxAge) between current and last stored
current and last stored failed login (300 seconds by default) or number failed login (300 seconds by default) edit ``lemonldap-ng.ini`` in [portal] section:
of allowed failed login attempts (3 by default) edit
``lemonldap-ng.ini`` in [portal] section:
.. code-block:: ini .. code-block:: ini
@ -72,7 +77,12 @@ of allowed failed login attempts (3 by default) edit
.. attention:: .. attention::
Number of failed login attempts history might be also higher than
number of incremental lock time value plus allowed failed login attempts.
Incremental lock time values list will be truncated if not.
.. danger::
Number of failed login attempts stored in history MUST Number of failed login attempts stored in history MUST
be higher than allowed failed logins for this plugin takes effect. be higher than allowed failed logins for this plugin takes effect.
See :doc:`History plugin<loginhistory>` See :doc:`History plugin<loginhistory>`

View File

@ -19,7 +19,7 @@ sub defaultValues {
'authentication' => 'Demo', 'authentication' => 'Demo',
'available2F' => 'UTOTP,TOTP,U2F,REST,Mail2F,Ext2F,Yubikey,Radius', 'available2F' => 'UTOTP,TOTP,U2F,REST,Mail2F,Ext2F,Yubikey,Radius',
'available2FSelfRegistration' => 'TOTP,U2F,Yubikey', 'available2FSelfRegistration' => 'TOTP,U2F,Yubikey',
'bruteForceProtectionLockTimes' => '5 15 60 300 600', 'bruteForceProtectionLockTimes' => '5, 15, 60, 300, 600',
'bruteForceProtectionMaxAge' => 300, 'bruteForceProtectionMaxAge' => 300,
'bruteForceProtectionMaxFailed' => 3, 'bruteForceProtectionMaxFailed' => 3,
'bruteForceProtectionMaxLockTime' => 900, 'bruteForceProtectionMaxLockTime' => 900,

View File

@ -636,7 +636,7 @@ sub attributes {
'type' => 'bool' 'type' => 'bool'
}, },
'bruteForceProtectionLockTimes' => { 'bruteForceProtectionLockTimes' => {
'default' => '5 15 60 300 600', 'default' => '5, 15, 60, 300, 600',
'type' => 'text' 'type' => 'text'
}, },
'bruteForceProtectionMaxAge' => { 'bruteForceProtectionMaxAge' => {

View File

@ -833,7 +833,7 @@ sub attributes {
}, },
bruteForceProtectionLockTimes => { bruteForceProtectionLockTimes => {
type => 'text', type => 'text',
default => '5 15 60 300 600', default => '5, 15, 60, 300, 600',
documentation => documentation =>
'Incremental lock time values for brute force attack protection', 'Incremental lock time values for brute force attack protection',
}, },

View File

@ -630,7 +630,8 @@ sub tree {
'notificationStorageOptions', 'notificationStorageOptions',
{ {
title => 'serverNotification', title => 'serverNotification',
help => 'notifications.html#notification-server', help =>
'notifications.html#notification-server',
nodes => [ nodes => [
'notificationServer', 'notificationServer',
'notificationDefaultCond', 'notificationDefaultCond',
@ -959,7 +960,10 @@ sub tree {
form => 'simpleInputContainer', form => 'simpleInputContainer',
nodes => [ nodes => [
'bruteForceProtection', 'bruteForceProtection',
'bruteForceProtectionTempo',
'bruteForceProtectionMaxFailed',
'bruteForceProtectionIncrementalTempo', 'bruteForceProtectionIncrementalTempo',
'bruteForceProtectionLockTimes',
] ]
}, },
'lwpOpts', 'lwpOpts',

View File

@ -254,8 +254,6 @@ sub tests {
return ( 1, "Cookie TTL should be higher or equal than one hour" ) return ( 1, "Cookie TTL should be higher or equal than one hour" )
unless ( $conf->{cookieExpiration} >= 3600 unless ( $conf->{cookieExpiration} >= 3600
|| $conf->{cookieExpiration} == 0 ); || $conf->{cookieExpiration} == 0 );
# Return
return 1; return 1;
}, },
@ -265,8 +263,6 @@ sub tests {
return ( -1, "Session timeout should be higher than ten minutes" ) return ( -1, "Session timeout should be higher than ten minutes" )
unless ( $conf->{timeout} > 600 unless ( $conf->{timeout} > 600
|| $conf->{timeout} == 0 ); || $conf->{timeout} == 0 );
# Return
return 1; return 1;
}, },
@ -278,8 +274,6 @@ sub tests {
) )
unless ( $conf->{timeoutActivity} > 59 unless ( $conf->{timeoutActivity} > 59
|| $conf->{timeoutActivity} == 0 ); || $conf->{timeoutActivity} == 0 );
# Return
return 1; return 1;
}, },
@ -292,8 +286,6 @@ sub tests {
if ( $conf->{timeoutActivity} if ( $conf->{timeoutActivity}
and $conf->{timeoutActivity} <= and $conf->{timeoutActivity} <=
$conf->{timeoutActivityInterval} ); $conf->{timeoutActivityInterval} );
# Return
return 1; return 1;
}, },
@ -338,8 +330,6 @@ sub tests {
return ( 1, "SMTP authentication failed" ) return ( 1, "SMTP authentication failed" )
unless $smtp->auth( $conf->{SMTPAuthUser}, unless $smtp->auth( $conf->{SMTPAuthUser},
$conf->{SMTPAuthPass} ); $conf->{SMTPAuthPass} );
# Return
return 1; return 1;
}, },
@ -441,8 +431,6 @@ sub tests {
unless ( $conf->{combination} ); unless ( $conf->{combination} );
return ( 0, 'userDB must be set to "Same" to enable Combination' ) return ( 0, 'userDB must be set to "Same" to enable Combination' )
unless ( $conf->{userDB} eq "Same" ); unless ( $conf->{userDB} eq "Same" );
# Return
return 1; return 1;
}, },
@ -482,8 +470,6 @@ sub tests {
"Auth::Yubikey_WebClient module is required to enable Yubikey" "Auth::Yubikey_WebClient module is required to enable Yubikey"
) if ($@); ) if ($@);
} }
# Return
return 1; return 1;
}, },
@ -521,8 +507,6 @@ sub tests {
unless ( $conf->{totp2fRange} ); unless ( $conf->{totp2fRange} );
return ( 1, "TOTP interval should be higher than 10s" ) return ( 1, "TOTP interval should be higher than 10s" )
unless ( $conf->{totp2fInterval} > 10 ); unless ( $conf->{totp2fInterval} > 10 );
# Return
return 1; return 1;
}, },
@ -570,7 +554,6 @@ sub tests {
|| $conf->{'totp2fSelfRegistration'} ); || $conf->{'totp2fSelfRegistration'} );
$msg = "A self registrable module should be enabled to require 2FA" $msg = "A self registrable module should be enabled to require 2FA"
unless ($ok); unless ($ok);
return ( 1, $msg ); return ( 1, $msg );
}, },
@ -583,8 +566,6 @@ sub tests {
return ( 0, "External 2F Validate command must be set" ) return ( 0, "External 2F Validate command must be set" )
unless ( defined $conf->{ext2FValidateCommand} ); unless ( defined $conf->{ext2FValidateCommand} );
} }
# Return
return 1; return 1;
}, },
@ -595,8 +576,6 @@ sub tests {
unless ( $conf->{formTimeout} > 30 ); unless ( $conf->{formTimeout} > 30 );
return ( 1, "XSRF form token TTL should not be higher than 2mn" ) return ( 1, "XSRF form token TTL should not be higher than 2mn" )
if ( $conf->{formTimeout} > 120 ); if ( $conf->{formTimeout} > 120 );
# Return
return 1; return 1;
}, },
@ -607,8 +586,6 @@ sub tests {
unless ( $conf->{issuersTimeout} > 30 ); unless ( $conf->{issuersTimeout} > 30 );
return ( 1, "Issuers token TTL should not be higher than 2mn" ) return ( 1, "Issuers token TTL should not be higher than 2mn" )
if ( $conf->{issuersTimeout} > 120 ); if ( $conf->{issuersTimeout} > 120 );
# Return
return 1; return 1;
}, },
@ -617,8 +594,6 @@ sub tests {
return 1 unless ( $conf->{portalDisplayResetPassword} ); return 1 unless ( $conf->{portalDisplayResetPassword} );
return ( 1, "Number of reset password retries should not be null" ) return ( 1, "Number of reset password retries should not be null" )
unless ( $conf->{passwordResetAllowedRetries} ); unless ( $conf->{passwordResetAllowedRetries} );
# Return
return 1; return 1;
}, },
@ -641,8 +616,18 @@ sub tests {
return ( 1, return ( 1,
'Number of failed logins must be higher than 2 to enable "BruteForceProtection" plugin' 'Number of failed logins must be higher than 2 to enable "BruteForceProtection" plugin'
) unless ( $conf->{failedLoginNumber} > 2 ); ) unless ( $conf->{failedLoginNumber} > 2 );
return ( 1,
# Return 'Number of failed logins history must be higher than allowed failed logins plus lock time values'
)
if ( $conf->{bruteForceProtectionIncrementalTempo}
&& $conf->{failedLoginNumber} <=
$conf->{bruteForceProtectionMaxFailed} +
$conf->{bruteForceProtectionLockTimes} );
return ( 1,
'Number of failed logins history must be higher than allowed failed logins'
)
unless ( $conf->{failedLoginNumber} >
$conf->{bruteForceProtectionMaxFailed} );
return 1; return 1;
}, },
@ -654,8 +639,6 @@ sub tests {
) )
unless ( $conf->{requireToken} unless ( $conf->{requireToken}
or $conf->{captcha_mail_enabled} ); or $conf->{captcha_mail_enabled} );
# Return
return 1; return 1;
}, },
@ -666,8 +649,6 @@ sub tests {
) )
if ( $conf->{impersonationRule} if ( $conf->{impersonationRule}
&& $conf->{contextSwitchingRule} ); && $conf->{contextSwitchingRule} );
# Return
return 1; return 1;
}, },
@ -691,8 +672,6 @@ sub tests {
return ( 1, return ( 1,
"BruteForceProtection plugin enabled WITHOUT persistent session storage" "BruteForceProtection plugin enabled WITHOUT persistent session storage"
) if ( $conf->{bruteForceProtection} ); ) if ( $conf->{bruteForceProtection} );
# Return
return 1; return 1;
}, },
@ -707,8 +686,6 @@ sub tests {
return ( 1, return ( 1,
"XML::LibXSLT module is required to enable old format notifications" "XML::LibXSLT module is required to enable old format notifications"
) if ($@); ) if ($@);
# Return
return 1; return 1;
}, },
@ -722,8 +699,6 @@ sub tests {
return ( 1, return ( 1,
"DateTime::Format::RFC3339 module is required to enable CertificateResetByMail plugin" "DateTime::Format::RFC3339 module is required to enable CertificateResetByMail plugin"
) if ($@); ) if ($@);
# Return
return 1; return 1;
}, },
@ -867,7 +842,6 @@ sub tests {
and $conf->{portal} !~ /^https:/ ); and $conf->{portal} !~ /^https:/ );
return 1; return 1;
}, },
}; };
} }

View File

@ -106,7 +106,10 @@
"browseTree":"تصفح الهيكل", "browseTree":"تصفح الهيكل",
"bruteForceProtection":"تفعيل", "bruteForceProtection":"تفعيل",
"bruteForceAttackProtection":"Brute-force attack protection", "bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times", "bruteForceProtectionIncrementalTempo":"Incremental lock",
"bruteForceProtectionLockTimes":"Incremental lock times",
"bruteForceProtectionMaxFailed":"Allowed failed logins",
"bruteForceProtectionTempo":"Lock time",
"cancel":"إلغاء", "cancel":"إلغاء",
"captcha_login_enabled":"التفعيل في استمارة تسجيل الدخول", "captcha_login_enabled":"التفعيل في استمارة تسجيل الدخول",
"captcha_mail_enabled":"التفعيل في إعادة تعيين كلمة المرور بواسطة استمارة البريد", "captcha_mail_enabled":"التفعيل في إعادة تعيين كلمة المرور بواسطة استمارة البريد",

View File

@ -106,7 +106,10 @@
"browseTree":"Browse tree", "browseTree":"Browse tree",
"bruteForceProtection":"Activation", "bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Brute-force attack protection", "bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times", "bruteForceProtectionIncrementalTempo":"Incremental lock",
"bruteForceProtectionLockTimes":"Incremental lock times",
"bruteForceProtectionMaxFailed":"Allowed failed logins",
"bruteForceProtectionTempo":"Lock time",
"cancel":"Abbrechen", "cancel":"Abbrechen",
"captcha_login_enabled":"Activation in login form", "captcha_login_enabled":"Activation in login form",
"captcha_mail_enabled":"Activation in password reset by mail form", "captcha_mail_enabled":"Activation in password reset by mail form",

View File

@ -106,7 +106,10 @@
"browseTree":"Browse tree", "browseTree":"Browse tree",
"bruteForceProtection":"Activation", "bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Brute-force attack protection", "bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times", "bruteForceProtectionIncrementalTempo":"Incremental lock",
"bruteForceProtectionLockTimes":"Incremental lock times",
"bruteForceProtectionMaxFailed":"Allowed failed logins",
"bruteForceProtectionTempo":"Lock time",
"cancel":"Cancel", "cancel":"Cancel",
"captcha_login_enabled":"Activation in login form", "captcha_login_enabled":"Activation in login form",
"captcha_mail_enabled":"Activation in password reset by mail form", "captcha_mail_enabled":"Activation in password reset by mail form",

View File

@ -106,7 +106,10 @@
"browseTree":"Parcourir l'arbre", "browseTree":"Parcourir l'arbre",
"bruteForceProtection":"Activation", "bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Protection contre les attaques par force brute", "bruteForceAttackProtection":"Protection contre les attaques par force brute",
"bruteForceProtectionIncrementalTempo":"Temps de verrouillage incrémentiels", "bruteForceProtectionIncrementalTempo":"Verrouillage incrémentiel",
"bruteForceProtectionLockTimes":"Temps de verrouillage incrémentiel",
"bruteForceProtectionMaxFailed":"Nombre d'échecs de connexion autorisés",
"bruteForceProtectionTempo":"Temps de verrouillage",
"cancel":"Annuler", "cancel":"Annuler",
"captcha_login_enabled":"Activation dans le formulaire d'authentification", "captcha_login_enabled":"Activation dans le formulaire d'authentification",
"captcha_mail_enabled":"Activation dans le formulaire de réinitialisation par mail", "captcha_mail_enabled":"Activation dans le formulaire de réinitialisation par mail",

View File

@ -106,7 +106,10 @@
"browseTree":"Naviga albero", "browseTree":"Naviga albero",
"bruteForceProtection":"Attivazione", "bruteForceProtection":"Attivazione",
"bruteForceAttackProtection":"Brute-force attack protection", "bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times", "bruteForceProtectionIncrementalTempo":"Incremental lock",
"bruteForceProtectionLockTimes":"Incremental lock times",
"bruteForceProtectionMaxFailed":"Allowed failed logins",
"bruteForceProtectionTempo":"Lock time",
"cancel":"Cancella", "cancel":"Cancella",
"captcha_login_enabled":"Attivazione nel modulo di login", "captcha_login_enabled":"Attivazione nel modulo di login",
"captcha_mail_enabled":"Attivazione della reimpostazione della password tramite modulo di posta", "captcha_mail_enabled":"Attivazione della reimpostazione della password tramite modulo di posta",

View File

@ -106,7 +106,10 @@
"browseTree":"Przeglądaj drzewo", "browseTree":"Przeglądaj drzewo",
"bruteForceProtection":"Aktywacja", "bruteForceProtection":"Aktywacja",
"bruteForceAttackProtection":"Ochrona przed atakiem siłowym", "bruteForceAttackProtection":"Ochrona przed atakiem siłowym",
"bruteForceProtectionIncrementalTempo":"Przyrostowe czasy blokady", "bruteForceProtectionIncrementalTempo":"Incremental lock",
"bruteForceProtectionLockTimes":"Incremental lock times",
"bruteForceProtectionMaxFailed":"Allowed failed logins",
"bruteForceProtectionTempo":"Lock time",
"cancel":"Anuluj", "cancel":"Anuluj",
"captcha_login_enabled":"Aktywacja w formularzu logowania", "captcha_login_enabled":"Aktywacja w formularzu logowania",
"captcha_mail_enabled":"Aktywacja przy resetowaniu hasła za pomocą formularza pocztowego", "captcha_mail_enabled":"Aktywacja przy resetowaniu hasła za pomocą formularza pocztowego",

View File

@ -106,7 +106,10 @@
"browseTree":"Ağaca göz at", "browseTree":"Ağaca göz at",
"bruteForceProtection":"Aktivasyon", "bruteForceProtection":"Aktivasyon",
"bruteForceAttackProtection":"Kaba kuvvet saldırı koruması", "bruteForceAttackProtection":"Kaba kuvvet saldırı koruması",
"bruteForceProtectionIncrementalTempo":"Artan gecikme zamanı", "bruteForceProtectionIncrementalTempo":"Incremental lock",
"bruteForceProtectionLockTimes":"Incremental lock times",
"bruteForceProtectionMaxFailed":"Allowed failed logins",
"bruteForceProtectionTempo":"Lock time",
"cancel":"İptal Et", "cancel":"İptal Et",
"captcha_login_enabled":"Giriş formunda aktivasyon", "captcha_login_enabled":"Giriş formunda aktivasyon",
"captcha_mail_enabled":"E-posta formu tarafından parola sıfırlamada aktivasyon", "captcha_mail_enabled":"E-posta formu tarafından parola sıfırlamada aktivasyon",

View File

@ -106,7 +106,10 @@
"browseTree":"Duyệt cây", "browseTree":"Duyệt cây",
"bruteForceProtection":"Kích hoạt", "bruteForceProtection":"Kích hoạt",
"bruteForceAttackProtection":"Brute-force attack protection", "bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times", "bruteForceProtectionIncrementalTempo":"Incremental lock",
"bruteForceProtectionLockTimes":"Incremental lock times",
"bruteForceProtectionMaxFailed":"Allowed failed logins",
"bruteForceProtectionTempo":"Lock time",
"cancel":"Hủy", "cancel":"Hủy",
"captcha_login_enabled":"Kích hoạt ở dạng đăng nhập", "captcha_login_enabled":"Kích hoạt ở dạng đăng nhập",
"captcha_mail_enabled":"Kích hoạt đặt lại mật khẩu bằng biểu mẫu thư", "captcha_mail_enabled":"Kích hoạt đặt lại mật khẩu bằng biểu mẫu thư",

View File

@ -106,7 +106,10 @@
"browseTree":"浏览树", "browseTree":"浏览树",
"bruteForceProtection":"激活", "bruteForceProtection":"激活",
"bruteForceAttackProtection":"Brute-force attack protection", "bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times", "bruteForceProtectionIncrementalTempo":"Incremental lock",
"bruteForceProtectionLockTimes":"Incremental lock times",
"bruteForceProtectionMaxFailed":"Allowed failed logins",
"bruteForceProtectionTempo":"Lock time",
"cancel":"取消", "cancel":"取消",
"captcha_login_enabled":" 登录激活", "captcha_login_enabled":" 登录激活",
"captcha_mail_enabled":"通过邮件进行密码重置 激活", "captcha_mail_enabled":"通过邮件进行密码重置 激活",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -24,10 +24,6 @@ my @notManagedAttributes = (
'sfEngine', 'available2FSelfRegistration', 'available2F', 'max2FDevices', 'sfEngine', 'available2FSelfRegistration', 'available2F', 'max2FDevices',
'max2FDevicesNameLength', 'max2FDevicesNameLength',
# Brute force attack protection parameters
'bruteForceProtectionMaxAge', 'bruteForceProtectionTempo',
'bruteForceProtectionMaxFailed',
# Handlers # Handlers
'handlerInternalCache', 'handlerServiceTokenTTL', 'handlerInternalCache', 'handlerServiceTokenTTL',
@ -42,8 +38,8 @@ my @notManagedAttributes = (
'syslogFacility', 'userLogger', 'logLevel', 'syslogFacility', 'userLogger', 'logLevel',
# Plugins parameters # Plugins parameters
'notificationsMaxRetrieve', 'persistentSessionAttributes', 'notificationsMaxRetrieve', 'persistentSessionAttributes',
'bruteForceProtectionLockTimes', 'bruteForceProtectionMaxLockTime', 'bruteForceProtectionMaxAge', 'bruteForceProtectionMaxLockTime',
# PSGI/CGI protection (must be set in lemonldap-ng.ini) # PSGI/CGI protection (must be set in lemonldap-ng.ini)
'protection', 'protection',

View File

@ -4,7 +4,7 @@ use strict;
use Mouse; use Mouse;
use Lemonldap::NG::Portal::Main::Constants qw(PE_OK PE_WAIT); use Lemonldap::NG::Portal::Main::Constants qw(PE_OK PE_WAIT);
our $VERSION = '2.0.8'; our $VERSION = '2.0.9';
extends 'Lemonldap::NG::Portal::Main::Plugin'; extends 'Lemonldap::NG::Portal::Main::Plugin';
@ -38,9 +38,9 @@ sub init {
unless ( $self->conf->{failedLoginNumber} > unless ( $self->conf->{failedLoginNumber} >
$self->conf->{bruteForceProtectionMaxFailed} ) $self->conf->{bruteForceProtectionMaxFailed} )
{ {
$self->logger->error( 'failedLoginNumber(' $self->logger->error( 'Number of failed logins history ('
. $self->conf->{failedLoginNumber} . $self->conf->{failedLoginNumber}
. ') must be higher than bruteForceProtectionMaxFailed(' . ') must be higher than allowed failed logins attempt ('
. $self->conf->{bruteForceProtectionMaxFailed} . $self->conf->{bruteForceProtectionMaxFailed}
. ')' ); . ')' );
return 0; return 0;
@ -48,20 +48,33 @@ sub init {
if ( $self->conf->{bruteForceProtectionIncrementalTempo} ) { if ( $self->conf->{bruteForceProtectionIncrementalTempo} ) {
my $lockTimes = @{ $self->lockTimes } = my $lockTimes = @{ $self->lockTimes } =
sort { $a <=> $b } sort { $a <=> $b }
map { $_ < $self->conf->{bruteForceProtectionMaxLockTime} ? $_ : () } map {
$_ =~ s/\D//;
$_ < $self->conf->{bruteForceProtectionMaxLockTime} ? $_ : ()
}
grep { /\d+/ } grep { /\d+/ }
split /\s+/, $self->conf->{bruteForceProtectionLockTimes}; split /\s*,\s*/, $self->conf->{bruteForceProtectionLockTimes};
unless ($lockTimes) { unless ($lockTimes) {
@{ $self->lockTimes } = ( 5, 15, 60, 300, 600 ); @{ $self->lockTimes } = ( 5, 15, 60, 300, 600 );
$lockTimes = 5; $lockTimes = 5;
} }
if ( $lockTimes > $self->conf->{failedLoginNumber} ) { for (
$self->logger->warn( 'Number of incremental lock time values (' my $i = 1 ;
. "$lockTimes) is higher than failed logins history (" $i <= $self->conf->{bruteForceProtectionMaxFailed} ;
$i++
)
{
unshift @{ $self->lockTimes }, 0;
$lockTimes++;
}
unless ( $lockTimes < $self->conf->{failedLoginNumber} ) {
$self->logger->warn( 'Number failed logins history ('
. $self->conf->{failedLoginNumber} . $self->conf->{failedLoginNumber}
. ')' ); . ') must be higher than incremental lock time values plus allowed failed logins attempt ('
. "$lockTimes)" );
splice @{ $self->lockTimes }, $self->conf->{failedLoginNumber}; splice @{ $self->lockTimes }, $self->conf->{failedLoginNumber};
$lockTimes = $self->conf->{failedLoginNumber}; $lockTimes = $self->conf->{failedLoginNumber};
} }
@ -96,9 +109,9 @@ sub run {
my $delta = $now - $lastFailedLoginEpoch; my $delta = $now - $lastFailedLoginEpoch;
$self->logger->debug(" -> Delta = $delta"); $self->logger->debug(" -> Delta = $delta");
my $waitingTime = $self->lockTimes->[ $countFailed - 1 ] my $waitingTime = $self->lockTimes->[ $countFailed - 1 ]
|| $self->conf->{bruteForceProtectionMaxLockTime}; // $self->conf->{bruteForceProtectionMaxLockTime};
$self->logger->debug(" -> Waiting time = $waitingTime"); $self->logger->debug(" -> Waiting time = $waitingTime");
unless ( $delta > $waitingTime ) { if ( $waitingTime && $delta <= $waitingTime ) {
$self->logger->debug("BruteForceProtection enabled"); $self->logger->debug("BruteForceProtection enabled");
$req->lockTime($waitingTime); $req->lockTime($waitingTime);
return PE_WAIT; return PE_WAIT;

View File

@ -26,6 +26,7 @@ SKIP: {
totp2fSelfRegistration => 1, totp2fSelfRegistration => 1,
totp2fActivation => 1, totp2fActivation => 1,
failedLoginNumber => 4, failedLoginNumber => 4,
bruteForceProtectionMaxFailed => 0,
} }
} }
); );

View File

@ -16,9 +16,10 @@ my $client = LLNG::Manager::Test->new( {
loginHistoryEnabled => 1, loginHistoryEnabled => 1,
bruteForceProtection => 1, bruteForceProtection => 1,
bruteForceProtectionIncrementalTempo => 1, bruteForceProtectionIncrementalTempo => 1,
failedLoginNumber => 4, failedLoginNumber => 6,
bruteForceProtectionMaxLockTime => 300, bruteForceProtectionMaxLockTime => 300,
bruteForceProtectionLockTimes => '5 500 bad 20 10 ', bruteForceProtectionLockTimes => '5 , 500, bad ,20, 10 ',
bruteForceProtectionMaxFailed => 2,
} }
} }
); );
@ -38,6 +39,36 @@ my $id = expectCookie($res);
expectRedirection( $res, 'http://auth.example.com/' ); expectRedirection( $res, 'http://auth.example.com/' );
$client->logout($id); $client->logout($id);
## First allowed failed login
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=ohwd'),
length => 23,
accept => 'text/html',
),
'1st allowed Bad Auth query'
);
ok( $res->[2]->[0] =~ /<span trmsg="5"><\/span>/,
'Bad credential' )
or print STDERR Dumper( $res->[2]->[0] );
count(2);
## Second allowed failed login
ok(
$res = $client->_post(
'/',
IO::String->new('user=dwho&password=ohwd'),
length => 23,
accept => 'text/html',
),
'2nd allowed Bad Auth query'
);
ok( $res->[2]->[0] =~ /<span trmsg="5"><\/span>/,
'Bad credential' )
or print STDERR Dumper( $res->[2]->[0] );
count(2);
## First failed connection ## First failed connection
ok( ok(
$res = $client->_post( $res = $client->_post(