I made a forum post about this and it was suggested that I should write about it here..
About once a week I get on my logs that someone has tried logging in to my site several times in a row, unsuccesfully. Maybe altering the flood-functionality (for example flood_is_allowed()) to allow the admin to set the maximum amount of login attempts per X minutes could be a good solution?
I'm a bit surprised this wasn't already implemented, correct me if I'm wrong.. seems like a good feature to me ;)
Comment | File | Size | Author |
---|---|---|---|
#19 | user_authenticate_hook.patch | 600 bytes | grendzy |
#18 | user_authenticate_hook.patch | 792 bytes | grendzy |
Comments
Comment #1
RobRoy CreditAttribution: RobRoy commentedFeatures go against latest development version. Sounds like a good idea.
Comment #2
Heine CreditAttribution: Heine commentedWhile this sounds good, it has one caveat: it makes a denial of service against an account (eg uid 1) fairly easy. Introducing a delay of several seconds, perhaps increasing slightly after multiple attempts, before displaying an error message might be a better idea.
Comment #3
Heine CreditAttribution: Heine commentedTo add: with delays, one needs to take into account that an attacker can use multiple clients / connections at the same time:
allowing 100 simultaneous, instantenous attemps and a 20 sec delay would limit an attacker to trying 432,000 passwords a day.
Comment #4
grendzy CreditAttribution: grendzy commentedI agree tho DOS issue needs to be considered. Perhaps this could be mitigated by banning the individual IP (instead of the entire user account) after the flood limit is exceeded? A large botnet could still overcome this limit... but an attacker with just a small number of machines / IPs would have a harder time.
432 000 seems like too many attempts to allow. A dictionary-based attack would be pretty likely to succeed with that many attempts.
Comment #5
tormu CreditAttribution: tormu commentedGlad to see that this is considered as a needful feature :)
For small sites with just about 20 users it would be sufficient to set that the individual IP can try for example 5 or 10 times per hour. If the user doesn't remember the password in 5 trys, it's already starting to feel like abnormal usage to me. Well, I hope you Drupal gurus get this thing rolling and implemented in the near future :)
Comment #6
Heine CreditAttribution: Heine commentedDiscussing something does not indicate endorsement.
No really, I'm not interested in trading one weak spot for another weak spot (blocking AOL proxies is generally not acceptable).
Comment #7
meba CreditAttribution: meba commentedTherefore you need to keep the delay against user account, not remote IP.
15 seconds (seems reasonable, 20 seconds is too close to PHP max execution time) delay after every 3 wrong attempts means 5760 attempts a day which is reasonable and something you will likely notice and do another action.
Comment #8
RobRoy CreditAttribution: RobRoy commentedFYI, this could easily be done as a contrib module so unless there is a great implementation idea and support, it may be better left for contrib.
Comment #9
Gurpartap Singh CreditAttribution: Gurpartap Singh commentedHow about a feature request in akismet module or take the bits from there for new module? It already implementing lots of stuff similar to this one.
Comment #10
bjaspan CreditAttribution: bjaspan commentedThis is a tough question.
I like the general idea: after X failed attempts in Y minutes, all login attempts will fail for Z minutes. This is not affected by the fact that an attacker can run simultaneous connections whereas pretty much any "slow down the login process" technique will either have no effect or will tie up extra resources on the server.
However, any such approach also necessarily leaves open a denial of service attack. Of course, just hammering Drupal with requests is a denial attack of its own, and there is nothing Drupal can do to protect itself from botnets, so it isn't clear that adding this additional denial vector is a real change.
One option would be not to enforce the max-failed-attempt rule against uid 1 on the assumption that the admin can be trusted to choose a good password (unlike most users who can't be).
Comment #11
webchickHm. Does it have to be either an IP or a username lockout? Could it not be a mixture of both?
Example:
I am 3vilh4x0r going through proxy.aol.com 1.2.3.4 and attempt to login as the "root" account 4 times in an hour.
I am 1nn0c3nt4ng3l going through proxy.aol.com 1.2.3.4 and attempt to login to the "angel" account.
Drupal blocks 1.2.3.4 from trying to login to the "root" account for an hour.
Drupal does NOT block 1.2.3.4 from trying to login to the "angel" account.
3vilh4x0r could still brute-force accounts, but it would become much more tedious. And innocent users of proxies don't get snagged.
The one snafu is if the real owner for "root" and 3vilh4x0r are on the same IP (which I guess could happen if you have an enemy at school or something), but that seems like a fairly minor problem to be concerned with.
Comment #12
bjaspan CreditAttribution: bjaspan commentedI realize that my previous message was unclear.
When I said, "after X failed attempts in Y minutes, all login attempts will fail for Z minutes," I meant that as applying all to a single account. X failed attempts to login as user foo in Y minutes will disable all logins for user foo for Z minutes. User bar is unaffected.
I think doing anything with IP addresses will be useless or worse. If we get X failed attempts for user foo in rapid succession, do we really care which IP address it is coming from?
OTOH, imagine a world in which brute-force login attempts are as common as Windows vulnerability scans (i.e. constant for every host on the net). In this case, do we really want to disable logins at all? I'm sure this situation is coming. Maybe we just have to accept password guessing as a fact of life and ignore it.
Comment #13
spiffster CreditAttribution: spiffster commentedWhy not implement a secondary question / answer after a password has been successfully guessed? Im sure this would discourage many of these douche bags. Also, it would help to lobby and support laws that execute offenders. :)
Comment #14
rorysolomon CreditAttribution: rorysolomon commentedWhat is the current status of this? Has a patch been installed or is there any plan for Drupal 6?
Thanks.
Comment #15
lilou CreditAttribution: lilou commentedBump.
Comment #16
duntuk CreditAttribution: duntuk commentedas indicated in #13, (challenge-response) captcha at login would be a viable solution...
http://drupal.org/project/captcha
Comment #17
tormu CreditAttribution: tormu commentedHaving to enter an answer to captcha every time I login would be quite annoying.. If there would be a way to introduce the captcha after the first incorrect answer, it would be a lot better way. That way most of the legitimate users never have to enter it, right?
Comment #18
grendzy CreditAttribution: grendzy commentedGood! This is probably the best idea so far. After X attempts in Y minutes, logins require a captcha (or perhaps a proof-of-work). Because captcha is in contrib, I don't think this approach is likely to become part of core, however I could see it as an additional feature of one (or both) of these modules:
http://drupal.org/project/captcha
http://drupal.org/project/hashcash
The big win is this at worst, a denial-of-service attack will only inconvenience the legit user with a captcha. Of course the captcha's are a constant arms race... but it will certainly make an attacker work a lot harder.
One thing we will have to watch out for: Login attempts may not always come through the /user form. There is also blogapi, and perhaps other means as well. Often other modules will call user_authenticate directly, which will bypass hook_user for failed login attempts.
What does everyone think about a patch for user_authenticate() that invokes
hook_user('authenticate', &$form_values, $account)
?
(I haven't tested this at all, just floating the idea. And of course the patch would have to include updated docs for hook_user with the extra $op, and probably a unit test).
Comment #19
grendzy CreditAttribution: grendzy commentedActually, that hook might be a little redundant, since we already have hook_user('login' ....). No point in making the hook implementor re-check the password to take action against bad logins.
Here's one that targets failed logins specifically.
Comment #20
Dave ReidWhy not just run a flood control when hook_user('login') is invoked n times for user x?
Comment #21
grendzy CreditAttribution: grendzy commented@Dave Reid: As I understand the current hook_user('login'...), it's only invoked on successful logins. So it can't be used to act on failed logins. There is hook_user('form'...), whereby an additional ['#submit'] callback could be added.
However, an attacker could bypass a form-based hook by hammering on the blogapi XML-RPC interface.
Or is there another way to use the existing hook I'm missing?
One other thought on hashcash... I tested the module, and while novel and interesting, it actually seems pretty useless as a brute-force countermeasure. The hashcash module requires (on average) 100 MD5 hashes to be computed per login. This is such a tiny amount of work that it would not slow an attack at all (MD5Crack can compute > 40 million hashes per second on current PCs). :-(
Comment #22
Dave Reid@grendzy, you're right. I guess right now I could go either way on an extra hook_user op, but I guess a better solution is to use hook_form_alter on the login form to add an extra validate function. The additional validation function implements a flood control, etc.
Comment #23
webchickTagging this so we can start collecting issues like this.
Comment #24
sunHow do others solve this? Easy - after X attempts, send and require an e-mail confirmation to re-enable the login.
Comment #25
Dave ReidHot dog...issue tagging! I didn't even notice it yet today!
@sun, From what I've encountered, if the same user account has had multiple bad attemps, the account is blocked. The flood control contrib module I'm working on is going to provide triggers when each flood control threshold is met so actions like block IP/block user actions can be assigned to them.
Comment #26
sun@Dave Reid: See above. btw: Did you lost your IRC nick credentials? ;)
Comment #27
gregglesSee http://drupal.org/project/login_security for a current example implementation.
I'm not sure that a time-out is a good idea for core.
Comment #28
pwolanin CreditAttribution: pwolanin commentedWe already have a flood control API that is used by contact module, for example. By default, we lock you out after 3 contact attempts for an hour. Why is the contact form subjected to higher security than login?! http://api.drupal.org/api/function/contact_personal_page/7
wrt this research article, bumping to critical:
http://www.usenix.org/event/hotsec07/tech/full_papers/florencio/florenci...
Seems like we should use (and maybe improve) the existing flood API?
Comment #29
pwolanin CreditAttribution: pwolanin commentedthis issue has a much more recent patch #485974: Improved security: rate limit login attempts.