Related Categories: Web Dev

How  do you prevent a brute force attacker (bot) from using a dictionary to attempt to log into your web application?  I have chatted a bit with others in the bloggoshere and read various sites and haven't come up with a solution.  (that doesn't require special web server software) Here is a good article reviewing the issue and possible options.

Obviously one of the best ways to protect against the dictionary attack is to force strong passwords.  However, that doesn't prevent the bot from trying anyway.   Plus we all know users say one thing and then do another when we are not looking.  So step one is writing code that forces strong passwords.

Strong passwords alone doesn't stop a bot.  At first I liked the idea of tracking sessions.  However it is way to easy for bots to simply not start a session in the first place.  In fact most good bots don't keep session.  So thats out.

So instead we could track a user account and simply deny login for that user after x number of failed attempts.  This just creates a very easy way for a bot to deny that user from ever logging in again.  We have built our own DOS attack for them.  So thats out.

We could track by IP.  That isn't perfect, but seems like the best option.   At that point we could deny that IP after x number of attempts, but that also could prevent real users from logging in.  Or we could incrementally delay the response of the login script for every failed attempt.  Slowing it down could really stop or delay that bot.  However there is nothing to say that the bot waits for a response before trying again.  The slowdown can't be client side and be effective for most web apps, and thus would probably just tie up server resources in the end.  (would flex or air solve this?)

So, what do other people do (if anything)?


Like this entry? Subscribe to my blog.

Comments (moderation on)

I think Michael Dinowitz came up with the following idea.
Given: The user has gotten to your login page from another page from your website (click here to login).
Then: cgi.http_referer should be your website address.
Not foolproof - but just another locked door.
<cfif cgi.http_referer NEQ MYWEBSITE>
<cfabort>
</cfif>
# Posted By Phillip Senn | 1/4/08 2:21 PM
Just subscribing...
# Posted By todd sharp | 1/4/08 2:26 PM
@phillip
I am glad you brought that up, as I meant to mention it. My issue with that is I found some firewalls and such now filter that out somehow. My visitor tracker shows that as empty a lot of the time and I KNOW they are not all manually typing in our address or clicking from a non browser program. Do others agree or disagree?

Also checking for browser type is also iffy as the bot can easily make itself look like a browser.
# Posted By joshua | 1/4/08 2:26 PM
Just had a thought.

My app requires session anyway. So couldn't I just upon login attempt test for session. If no session kick them back to login screen (bad bot or user). If session exists, then attempt that login. If login bad set the session.loginattempt = +1 and after GT 10 or something like that then lock em out for 30 min or something.

The issue i see with this is can't a bot just start a new session each attempt? I suppose it would require them to start at login page and just submit over and over (instead of submitting directly to action page).
# Posted By joshua | 1/4/08 2:33 PM
My momma always says
"System generated passwords are like a box of chocolates. You never know which one you're gonna get."
# Posted By Phillip Senn | 1/4/08 2:52 PM
One thing I've seen suggested is a sliding timeout period. Basically just ignore subsequent requests if they come in within a certain time period of the previous request. For each accepted login attempt that fails, increase the timeout period. You would also need to have an automatic unlock period, say 30 minutes, that would unlock a users account.

Say you have a minimum time between login attempts set to 15-30 seconds. Any repeated requests that come in under that are ignored. You also have another time period within which invalid login attempts are logged. That time period increases as the number of logged failed login attempts increases.

1. Login is attempted (invalid)
2. Invalid attempt is logged and login count increased
3. Another invalid login is attempted within minimum time period
4. This invalid login attempt is logged, but the count is not increased

Let's say you have the timeout values set at 15 and 60 seconds initially. That would allow four invalid login attempts to be counted every minute. If repeated attempts are made quickly, though, the account wouldn't be locked, and the attempt wouldn't be processed. After the 60 second period expires, failed logins would again be counted, but that time period would increase to say 120 seconds. That way the failed login attempts would be ignored even longer if they come in rapidly. That period is increased until you reach the maximum number of login attempts.

Obviously no process is foolproof. If the bot just keeps trying, it will eventually lock the account. If it only runs for a little while, it may increase the failed attempts a couple times, but not lock it. It may then come back later.

Repeated failed attempts from an IP could be blocked for a short time period. That would help stop them, and open the door back up if the IP happens to be shared.

As far as referrers, a lot ot tech savvy people change their browser to not send the referrer. It's probably pretty easy in everything but IE. I've never heard of a firewall stripping out an http-referer header. I don't think that mandating that header from your users is a bad idea. Though it can obviously be forged.

You could also have a hidden form field with a value that is known to the server. If it doesn't exist and match what's on the server, the login attempt should be ignored. A bot could send the field, but it would be guessing as to what its value should be.
# Posted By Steve | 1/4/08 2:54 PM
When I ran a hosting company, we blocked bot attacks by IP address. When any attempt to log into a server (SSH, Telnet, FTP, etc.) failed 3 times, their IP address would be added to a blacklist. This kept any bots from being able to even reach the servers anymore. It was harsh, but necessary.

If any real customer happened to forget a password and get blocked, the list of was available via our CP, so we could just go in and unblock them anytime.

BlogCFC itself has a list of blocked IPs. Open up blog.ini.cfm and there's an entry for ipblocklist.

Can you guess what my vote is? :)
# Posted By Adrian J. Moreno | 1/4/08 3:09 PM
@ steve

I rather like the idea of a hidden form field. Take the session.uuid (created at start of session) and session.logginattempt (defalt 0). hash the uuid with the logginattempt as the salt. then store that as the hidden form field. Upon submit check that it exists and is correct. if fail, then increment session.logginattempt and use that increment as salt for the hash then use that new hash as the new hidden value, repeat. after session.logginattempt GT 5 lock em for the duration of the session.

I think that would force sessions, prevent bots. Some joker could manually type, but only a few times before being locked out. This way it doesn't lock real users from trying. And nothing to store / query from db other than what we already need for authentication.
# Posted By joshua | 1/4/08 3:11 PM
I know this is maybe not what you had in mind, but my bank forces me to "Verify" every computer that I use to login. Basically, if I try to login on a new computer, the bank has to either SMS Text me or email me a verification code based on the computer I am using (some combo of IP address or whatever it uses). Once I have that, I can login from that computer going forward.

It's a bit of a pain when ever I need to use a new computer, but it seems like pretty good security against brute force as it is more more secret value that it would have to hack.
# Posted By Ben Nadel | 1/4/08 3:41 PM
My bank does that too. I hate it :) But me hating it is better then my bank account being compromised.
# Posted By todd sharp | 1/4/08 3:55 PM
Hey Ben,

I think that is another good layer. Not a solution, but for some apps a good additional mode of protection.

My bank shows (during login) an image and text that I had previously associated with that image. That way its easy to tell if I am at a fishing site since they wouldn't have the image that I selected or the image I associated with it. ThoughI kind of doubt most people would even notice if the image wasn't shown or showed the wrong text.
# Posted By joshua | 1/4/08 3:56 PM
About IP addresses, keep in mind that many hosts like AOL cause the user's IP to be all over the place, 2 requests from the same user can come from different IPs. These users also share those IP's with other users.
# Posted By ian | 1/4/08 8:02 PM

Sponsors


Savvy Content Manager