7

in a setting where one has forgotten their password, I'd like to be able to limit the attempts of entering in email addresses to something like 10. My first thought was to use a cookie.

$attempts = 0;

if( isset( $_COOKIE['password_recovery_attempts'] ) ) 
  $attempts = $_COOKIE['password_recovery_attempts'];

if( $pass === false ){
  $val = $attempts + 1;
  setcookie( 'password_recovery_attempts', $val, time() + 86400 ); //86400 = 1 day
}

if( $attempts > 9 ){
  echo 'You have attempted to reset your password too many times';
  return false;
}

My concern with only using a cookie, is that cookies can easily be removed. What would be a better practice here?

Adam Caudill
  • 1,794
  • 14
  • 18
Tony
  • 71
  • 1
  • 3

3 Answers3

11

You're right that a cookie is a bad idea, but the approach itself is misguided.

The problem with these kinds of hard limits is that they make it easy for an attacker to create a DoS condition for the legitimate user, simply by putting in 10 incorrect email addresses. Even if you time-delay the attempts, the attacker can simply send a request every time the time limit lapses.

The way I'd implement it would be to time-limit the requests on a per-IP, per-account basis, with no delay after the first and second attempts, but increasing delays on consecutive attempts up to a limit of 45 seconds. Another useful measure would be to enforce 45 second delays on any request coming from an IP address that has seen more than 10 incorrect attempts in the last 15 minutes across any number of accounts, in order to protect against attacks that scan the entire list of usernames against a single known email address. I might also consider requiring a CAPTCHA after a certain number of failed attempts, to protect from automated attacks.

This provides the following benefits:

  • A legitimate client will always be able to use the feature (i.e. no DoS condition can be reached) as long as the attacker is using a different publicly-facing IP address.
  • A reasonable number of failed attempts can be performed without the user being hindered by lock-out mechanisms.
  • Both targeted attacks and low-hanging fruit attacks are hindered with minimal negative effects on legitimate users.
  • The worst-case scenario is that an attacker generates a DoS condition when on the same network as a legitimate user. Not exactly critical or likely.

This would involve storing failed attempts in a log on the server side, perhaps in a database table or in-memory cache.

Polynomial
  • 132,208
  • 43
  • 298
  • 379
  • Couldn't the attacker spoof his IP address in the packet headers? – MatsT Mar 13 '13 at 10:01
  • 1
    @MatsT Typically not. Traffic sent *to* the spoofed IP would have to be *received* by the attacker, *or* the attacker would have to make hard educated guesses about what the server sent. Here, authentication appears to be conducted by a web application. HTTP uses TCP. A TCP connection must be made between the client and the server before any authentication attempt. To make a TCP connection the [3-way handshake](http://en.wikipedia.org/wiki/Three_way_handshake#Connection_establishment) must succeed, so the attacker must reply to the server based on data received from it (usually hard to guess). – Eliah Kagan Mar 13 '13 at 10:22
  • 1
    @MatsT If authentication takes places over an SSL-secured connection (i.e., HTTPS), **which it should** (but which is still often not done), then the difficulty in submitting even an intentionally wrong authentication attempt from a spoofed IP address goes from *very hard* to *virtually impossible*. – Eliah Kagan Mar 13 '13 at 10:23
1

You can never trust the client, and using a cookie is just that - you are relying on the client (possibly an attacker) sending you accurate information. For this to work properly, you'd need to store this server side.

You could store the reset attempt count with the account data, and just reset it to 0 on successful login - workable for smaller sites. For larger sites, you'd want to store the data in a separate data store - memcached or similar (probably using whatever mechanism you use for server side sessions data).

Adam Caudill
  • 1,794
  • 14
  • 18
1

You should lock down the account server side. Setting a cookie or temporarily banning an IP can all be trivially circumvented by the client. I would suggest working a mechanism to disable the user from resetting after x failed attempts. Your PHP script should check your database if attempts exceed x.

aus
  • 188
  • 1
  • 8
  • 1
    I somewhat disagree with the first part - an IP ban isn't trivial to bypass, especially if you're already actively restricting Tor clients. – Polynomial Mar 13 '13 at 03:24
  • Vanilla IP ban is pretty trivial to circumvent. Banning Tor clients complicates thing, for sure. But if the attacker has his own botnet, he would have thousands of IPs at his fingertips for use. But nevertheless, any additional layer of protection would help. – aus Mar 13 '13 at 03:28
  • 1
    If the attacker has his own botnet, a DoS via repeated failed email attempts seems pretty low on your list of problems. – Polynomial Mar 13 '13 at 03:30