You want to prevent an attacker from making too many attempts at guessing a password through normal interactive means.
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.
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:
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.
In general, it is reasonable to allow one or two failed attempts before instituting delays, depending on the maximum number of allowed authentication failures.
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.
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; }