I recently added a neat security feature to Browserling. The feature is very simple - if someone tries to login into Browserling unsuccessfully, they have to wait a little bit until they can login again. This feature prevents automated password guessing.

Here's how the implementation looks like:


You've to wait 2n seconds between logins if you fail logging in n consecutive times.

The implementation of this security feature is just 20 lines long. Anyone can implement it in 15 minutes. The basic logic is the following - if a login is unsuccessful, then increase the unsuccessful-logins counter (n) for the user by one. If the login is unsuccessful again, figure out the time delta (delta) between the two logins in seconds and compare it to 2n. If the time delta is less than 2n, then make the user wait 2n - delta seconds. Otherwise reset the counter and log the user in.

A downside to this feature is that someone can lock someone else out of their account by trying to login too many times unsuccessfully. But in my opinion there is no reason why your web application shouldn't have this. You don't want your users' passwords to be hacked.

Until next time!

Comments

February 09, 2015, 02:35

What are your thoughts on doing this at an IP address level versus a username level?

February 09, 2015, 02:47

Blocking at IP level wouldn't be such a good idea because anyone who's serious about breaking a password would get thousands of IPs (proxy lists) and keep changing the IPs as soon as they get blocked. So they'd get many more tries. Instead of 15-20 tries, they'd get thousands of tries, which would be enough to crack simple dictionary passwords.

Arkady Permalink
February 09, 2015, 03:43

You should probably set an upper limit on the delay -- 60, or even 30 seconds should be plenty to prevent [successful] automated guessing while avoiding the problem of Mallory locking Alice out with just a few deliberately unsuccessful attempts.

February 11, 2015, 01:39

I've actually capped it at 1 day. 30 and 60 seconds still give too many retries. 1440 retries per day at 60 seconds per retry. Too many.

korkijs Permalink
February 09, 2015, 12:07

Your solution is vulnerable to side channel (time) user enumeration attack.

Old solution (not mine), that I liked so much, that started re-using in my projects: your system most likely anyhow needs audit log with success/failure states captured, so why duplicating this data, just select by user the needed data from table/data structure containing this log for somewhat short time interval, let's say everything for last 24h and the returned count of failed authentications goes into exponentially growing function, just like in your solution, which determines the delay at server side, before the user:pwd pair is checked.
Keeping in this log failed authentication records for invalid user names is needed from audit log completeness perspective, but as well effectively defends the solution against valid user name enumeration, thought with my today's knowledge I would say, that adding some little random calculation might be better to completely remove even in theory the ability do detect the deltas in time between valid and invalid user authentication attempts.

February 09, 2015, 19:44

Here are my two cents:
- This is a good solution if an attacker tries to brute force one specific user's password. However often they are only interested in getting any working user / password combination. If so, they can enumerate over all known users / a generic user list and just try the three most widely used passwords. With this attack they will most likely be able to access someones account without being limited by your solution.
- Furthermore your solution might allows an attacker to validate a username. He just has to enumerate over his generic user list and see if the user gets rate limited if you login with a dummy password multiple times. If so, it's a valid user.

February 11, 2015, 01:37

Korkijs and Florian:

My implementation doesn't have a timing attack. The delays are inserted for any email that you try. For example asdajjdj2@aksdkske.com would still have a pause of 2^n seconds.

However you should note that login discovery is always possible if the login is email. You just try to register with the email to find out if it's taken. If it is, the system won't let you register with that email.

February 11, 2015, 14:43

Hi Peter,

Thanks for the update - I didn't get that before.
Anyway, you're doing a fantastic job with Browserling!

February 24, 2015, 12:54

Hey guys

I need your feedback about my modular framework for Node.js
http://twee.io/

What do you think about it, is it possible for all of you to try it and give me feedback and advice of what can I improve to make it better?
Here is also video about installation and it's structure:
https://www.youtube.com/watch?v=3JxDQ0p0Fyg

I want to know if you like an Idea of it, then what can I do more to make it better for community needs.

Thanks you!

March 01, 2015, 01:16

Looks good.

Sanaya Permalink
February 28, 2015, 05:42

You should probably use Memcached or Redis. You don't really want to be hitting your MySQL database with rate limiting stuff. Your database will get crushed when a distributed cracking attempt begins on your login page.

correct me if i am wrong.
Thanks

March 01, 2015, 01:14

I don't have time right now to think about that. I use MariaDB for just about everything and it works.

Leave a new comment

(why do I need your e-mail?)

(Your twitter handle, if you have one.)

Type the word "computer_450": (just to make sure you're a human)

Please preview the comment before submitting to make sure it's OK.

Advertisements