8.8 Throttling Failed Authentication Attempts
8.8.1 Problem
You want to prevent an
attacker from making too many attempts at guessing a password through
normal interactive means.
8.8.2 Solution
It's best to use a protocol where such attacks
don't leak any information about a password, such as
a public key-based mechanism.
Delay program execution after a failed authentication attempt. For
each additional failure, increase the delay before allowing the user
to make another attempt to authenticate.
8.8.3 Discussion
Throttling failed authentication attempts is a balance between
allowing legitimate users who simply mistype a password or passphrase
to have a quick retry and delaying attackers who are trying to
brute-force passwords or passphrases.
Our recommended strategy has three variables that control how it
delays repeated authentication attempts:
- Maximum number of attempts
-
If this limit is reached, the
authentication should be considered a complete failure, resulting in
a disconnection of the network connection or shutting down of the
program that requires authentication. A reasonable limit on the
maximum number of allowed authentication attempts is three, or
perhaps five at most.
- Maximum number of failed attempts allowed before enabling throttling
-
In general, it is reasonable to allow one or two failed attempts
before instituting delays, depending on the maximum number of allowed
authentication failures.
- Number of seconds to delay between successive authentication attempts
-
For each successive failure,
the delay increases exponentially. For example, if the base number of
seconds to delay is set to two, the first delay will be two seconds,
the second delay will be four seconds, the third delay will be eight
seconds, and so on. A reasonable starting delay is generally one or
two seconds, but depending on the settings you choose for the first
two variables, you may want to increase the starting delay. In
particular, if you allow a large number of attempts, it is probably a
good idea to increase the delay.
The best way to institute a delay depends entirely upon the
architecture of your program. If authentication is being performed
over a network in a single-threaded server that is multiplexing
connections with select( ) or poll(
), the best option may be to compute the future time at
which the next authentication attempt will be accepted, and ignore
any input until that time arrives.
When authenticating a user interactively on a terminal on Unix, the
best solution is likely to be to use the sleep( )
function. On Windows, there is no strict equivalent. The Win32 API
functions Sleep( ) and SleepEx(
) will both return immediately—regardless of the
specified wait time—if there are no other threads of equal
priority waiting to run.
|
Some of these techniques can increase the risk of denial-of-service
attacks.
|
|
In a GUI environment, any authentication dialog presented to the user
will have a button labeled "OK" or
some equivalent. When a delay must be made, disable the button for
the duration of the delay, then enable it. On Windows, this is easily
accomplished using timers.
The following function, spc_throttle(
),
computes the number of seconds to delay based on the three variables
we've described and the number of failed
authentication attempts. It has four arguments:
- attempts
-
Pointer to an integer used to count the number of failed attempts.
Initially, the value of the integer to which it points should be
zero, and each call to spc_throttle( ) will
increment it by one.
- max_attempts
-
Maximum number of attempts to allow. When this number of attempts has
been made, the return from spc_throttle( ) will be
-1 to indicate a complete failure to authenticate.
- allowed_fails
-
Number of attempts allowed before enabling throttling.
- delay
-
Base delay in seconds.
If the maximum number of attempts has been reached, the return value
from spc_throttle( ) will be -1. If there is to be
no delay, the return value will be 0; otherwise, the return value
will be the number of seconds to delay before allowing another
authentication attempt.
int spc_throttle(int *attempts, int max_attempts, int allowed_fails, int delay) {
int exp;
(*attempts)++;
if (*attempts > max_attempts) return -1;
if (*attempts <= allowed_fails) return 0;
for (exp = *attempts - allowed_fails - 1; exp; exp--)
delay *= 2;
return delay;
}
|