From: Dietmar Maurer Date: Mon, 18 Nov 2013 08:05:04 +0000 (+0100) Subject: prevent user enumeration attacks X-Git-Url: https://git.proxmox.com/?p=pve-access-control.git;a=commitdiff_plain;h=6126ab75a0837298427491ea64b9b2e1139c6ba6;ds=sidebyside prevent user enumeration attacks --- diff --git a/PVE/API2/AccessControl.pm b/PVE/API2/AccessControl.pm index 1679ed4..8daf10c 100644 --- a/PVE/API2/AccessControl.pm +++ b/PVE/API2/AccessControl.pm @@ -2,6 +2,7 @@ package PVE::API2::AccessControl; use strict; use warnings; +use Time::HiRes qw(usleep gettimeofday tv_interval); use PVE::Exception qw(raise raise_perm_exc); use PVE::SafeSyslog; @@ -265,6 +266,8 @@ __PACKAGE__->register_method ({ my $res; + my $starttime = [gettimeofday]; + eval { # test if user exists and is enabled $rpcenv->check_user_enabled($username); @@ -279,7 +282,13 @@ __PACKAGE__->register_method ({ if (my $err = $@) { my $clientip = $rpcenv->get_client_ip() || ''; syslog('err', "authentication failure; rhost=$clientip user=$username msg=$err"); - die $err; + # do not return any info to prevent user enumeration attacks + # always try to delay exactly 3 seconds to prevent timing attacks + my $elapsed; + while (($elapsed = tv_interval($starttime)) < 3) { + usleep(int((3 - $elapsed)*1000000)); + } + die "authentication failure\n"; } $res->{cap} = &$compute_api_permission($rpcenv, $username); diff --git a/PVE/AccessControl.pm b/PVE/AccessControl.pm index ab5e52e..236efac 100644 --- a/PVE/AccessControl.pm +++ b/PVE/AccessControl.pm @@ -306,6 +306,7 @@ sub check_user_enabled { } # password should be utf8 encoded +# Note: some pluging delay/sleep if auth fails sub authenticate_user { my ($username, $password) = @_; @@ -317,32 +318,19 @@ sub authenticate_user { my $usercfg = cfs_read_file('user.cfg'); - eval { check_user_enabled($usercfg, $username); }; - if (my $err = $@) { - sleep(2); - die $err; - } + check_user_enabled($usercfg, $username); my $ctime = time(); my $expire = $usercfg->{users}->{$username}->{expire}; - if ($expire && ($expire < $ctime)) { - sleep(2); - die "account expired\n" - } + die "account expired\n" if $expire && ($expire < $ctime); my $domain_cfg = cfs_read_file('domains.cfg'); - eval { - my $cfg = $domain_cfg->{ids}->{$realm}; - die "auth domain '$realm' does not exists\n" if !$cfg; - my $plugin = PVE::Auth::Plugin->lookup($cfg->{type}); - $plugin->authenticate_user($cfg, $realm, $ruid, $password); - }; - if (my $err = $@) { - sleep(2); # timeout after failed auth - die $err; - } + my $cfg = $domain_cfg->{ids}->{$realm}; + die "auth domain '$realm' does not exists\n" if !$cfg; + my $plugin = PVE::Auth::Plugin->lookup($cfg->{type}); + $plugin->authenticate_user($cfg, $realm, $ruid, $password); return $username; }