I saw an article on dzone.com today that talked about ways to restrict logins after a number of unsuccessful attempts. I thought I'd show a quick demo of how to do this in ColdFusion. This method is not perfect and I'll talk about alternatives. I will also link to the original article so you can see how it was done there.
So - the concept is simple. If a user tries to log on N times, and fails, you want to prevent them from logging in again. This prevents a hacker (or script kiddie) from spending all day trying to guess a user's password.
You don't want to block the user forever of course, but just for a period of time. So let me show you the code I used for this demo, and then I'll talk about it. (A quick note - I wrote a one page demo for this post. On a real site this code may be broken up a bit into multiple files.)
<cfapplication name="test" sessionmanagement="true" clientmanagement="true">
<cfparam name="session.loginattempts" default="0">
<cfif structKeyExists(form, "logon") and structKeyExists(form, "username") and structKeyExists(form, "password") and session.loginattempts lt 3> <cfif false> <!--- log the user in ---> <cfset session.loginattempts = 0> <cfelseif true> <cfset session.loginattempts = session.loginattempts + 1> </cfif> </cfif>
<cfif session.loginattempts lt 3> <cfoutput> <form action="#getFileFromPath(getCurrentTemplatePath())#" method="post"> </cfoutput> username: <input type="text" name="username"><br> password: <input type="password" name="password"><br> <input type="submit" name="logon" value="Logon"> </form> <cfelse> Sorry, but you have attempted to logon too many times.<br> You are either very stupid or a hacker. Please go away. </cfif>
This is pretty self-explanatory. I initialize a session variable called loginattempts. (This would normally be done in the onSessionStart method of your Application.cfc file.) When the user attempts to login and they fail, I increase this value.
Later on I notice if the loginattempts value is too high and I display a message instead of the login form. As a side note - it occurred to me while writing this entry that I checked the value on display but did not check it when the form was submitted. I modified my initial CFIF to add that last clause. That way the user can't just hit "Back" and continue to try to login, or write their own form on their desktop and post from there.
So - this works easily enough but has a few problems. First - it uses session variables. It takes me all of two seconds to clear those values by using the Firefox Web Developer toolbar. Or I could switch to the Browser that Shalt not be Named. This technique would stop some script kiddies, but not someone who was very serious.
Instead of using the session scope (or some other cookie), you could check the IP address of the person making the request. This could be stored in a simple struct where each key is the IP address and the value is the number of login attempts. However, you would also need to store the time of the last login attempt. The nice thing about the session variable is that it will automatically go away when the session dies, thereby allowing the user another chance to login.
This method would not be full proof as you can't always trust the IP, and if the server were to go down you would lose the Application scope anyway. (Of course, it's hard to hack into a site that is down. It would only matter if your site actually came back up.)
One more idea - and what the original article did (linked below) - is to log the attempts to a database. This gives you the added benefit of being able to do tracking later on. If you see numerous daily failures from one IP, it is probably something you want to check out.
Anyway - enjoy - and readers - feel free to share how you did this in your own applications.
The original dzone.com article: Blocking access to the login page after three unsuccessful login attempts