Start of work on password reset rate limiting

This commit is contained in:
Jamie Cameron
2025-04-20 10:54:39 -07:00
parent a04646e785
commit cf50dd2431
3 changed files with 32 additions and 3 deletions

View File

@ -1616,6 +1616,7 @@ if (opendir(DIR, $main::forgot_password_link_dir)) {
my $cutoff = time() - 24*60*60;
foreach my $f (readdir(DIR)) {
next if ($f eq "." || $f eq "..");
next if ($f eq "ratelimit");
my $path = $main::forgot_password_link_dir."/".$f;
my @st = stat($path);
if ($st[9] < $cutoff) {

View File

@ -19,15 +19,42 @@ $wuser && $wuser->{'email'} || &error($text{'forgot_euser'});
$wuser->{'pass'} eq '*LK*' && &error($text{'forgot_elock'});
my $email = $wuser->{'email'};
# XXX rate limiting!
# Check if the IP or Webmin user is over it's rate limit
&make_dir($main::forgot_password_link_dir, 0700);
my $ratelimit_file = $main::forgot_password_link_dir."/ratelimit";
&lock_file($ratelimit_file);
my %ratelimit;
&read_file($ratelimit_file, \%ratelimit);
my $now = time();
my $rlerr;
foreach my $key ($ENV{'REMOTE_ADDR'}, $wuser->{'name'}, $wuser->{'email'}) {
if (!$ratelimit{$key."_last"} ||
$ratelimit{$key."_last"} < $now-5*60) {
# More than 5 mins since the last try, so reset counter
$ratelimit{$key} = 1;
}
else {
$ratelimit{$key}++;
}
$ratelimit{$key."_last"} = $now;
if ($ratelimit{$key} > 10) {
# More than 10 attempts in the last 5 minutes!
$rlerr = &text('forgot_erate',
"<tt>".&html_escape($key)."</tt>");
last;
}
}
# XXX clean up old keys
&write_file($ratelimit_file, \%ratelimit);
&unlock_file($ratelimit_file);
&error($rlerr) if ($rlerr);
# Generate a random ID for this password reset
my %link = ( 'id' => &generate_random_id(),
'remote' => $ENV{'REMOTE_ADDR'},
'time' => time(),
'time' => $now,
'user' => $wuser->{'name'} );
$link{'id'} || &error($text{'forgot_erandom'});
&make_dir($main::forgot_password_link_dir, 0700);
&write_file("$main::forgot_password_link_dir/$link{'id'}", \%link);
my $baseurl = &get_webmin_email_url();
my $url = $baseurl.'/forgot.cgi?id='.&urlize($link{'id'});

View File

@ -176,6 +176,7 @@ forgot_udoing=Changing password for Unix user $1 ..
forgot_eunix=Unix user does not exist!
forgot_eunixlock=User user's password is locked!
forgot_elogin=Forgotted password pages cannot be used when you are already logged in to Webmin!
forgot_erate=Too many password reset attempts for $1! Please try again later.
pam_header=Login to Webmin
pam_mesg=You must respond to the question below to login to Webmin server on $1.