I'm wondering how and what is the best way to lock user account after X times failed logins? I have table where I keep track of users failed login attempts. Table stores time stamp, username, ip address and browser type. After I detect incorrect login information, cfquery will pull records from failed login table based on username or IP address. If there is 5 or more invalid attempts I set account for inactive. Now I would like to somehow set timer that will start counting 5 minutes since last invalid attempt for that user. Then account should change the status to active. Here is my code that I have so far:
<cfquery name="checkUser" datasource="#dsn#">
SELECT UserName, Password, Salt, LockedUntil
FROM Users
WHERE UserName = <cfqueryparam cfsqltype="cf_sql_varchar" value="#trim(FORM.username)#" maxlength="50">
AND Active = 1
</cfquery>
<cfif len(checkUser.LockedUntil) AND dateCompare(now(), checkUser.LockedUntil,'n') EQ -1>
<cfset fnResults.status = "400">
<cfset fnResults.message = "This account is locked for 5 min.">
<cfreturn fnResults>
<cfabort>
</cfif>
<cfset storedPW = checkUser.Password>
<cfset enteredPW = FORM.password & checkUser.Salt>
<cfif checkUser.recordCount NEQ '1' OR (hash(enteredPW,"SHA-512") NEQ storedPW>
<cfquery name="logFail" datasource="#dsn#">
INSERT INTO FailedLogins(
LoginTime,
LoginUN,
LoginIP,
LoginBrowser
)VALUES(
CURRENT_TIMESTAMP,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#FORM.username#" maxlength="50">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#REMOTE_ADDR#" maxlength="20">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#CGI.HTTP_USER_AGENT#" maxlength="500">
)
</cfquery>
<!--- Pull failed logins based on username or IP address. --->
<cfquery name="failedAttempts" datasource="#dsn#">
SELECT LoginTime
FROM FailedLogins
WHERE LoginUN = <cfqueryparam cfsqltype="cf_sql_varchar" value="#trim(FORM.username)#" maxlength="50">
OR LoginIP = <cfqueryparam cfsqltype="cf_sql_varchar" value="#REMOTE_ADDR#" maxlength="20">
</cfquery>
<cfif failedAttempts.recordcount LT 4>
<cfset fnResults.status = "400">
<cfset fnResults.message = "Invalid Username or Password!">
<cfelseif failedAttempts.recordcount EQ 4>
<cfset fnResults.status = "400">
<cfset fnResults.message = "This is your last attempt. If you fail to provide correct information account will be locked!">
<cfelseif failedAttempts.recordcount GTE 5>
<cfset lockUntil = DateAdd('n', 5, now())>
<cfquery name="blockUser" datasource="#dsn#">
UPDATE Users
SET LockedUntil = <cfqueryparam cfsqltype="cf_sql_timestamp" value="#lockUntil#">
WHERE UserName = <cfqueryparam cfsqltype="cf_sql_varchar" value="#trim(FORM.username)#" maxlength="50">
</cfquery>
<cfset fnResults.status = "400">
<cfset fnResults.message = "This account is locked for 5 min.">
</cfif>
<cfelse>
//Clear failed login attempts
//Update lockedUntil field to NULL
//User logged in authentication successful!
</cfif>
After account is set to inactive / locked what would be the best way to set time count down and change the flag status? I saw some people recommended SQL Job but I'm not sure how often job should run and how to create that statement? If anyone can provide some example please let me know. Thank you.