如何锁定用户帐户一段时间?

时间:2018-03-19 19:20:36

标签: sql sql-server-2008 coldfusion locking unlock

我想知道在X次登录失败后如何以及锁定用户帐户的最佳方式是什么?我有表,我跟踪用户登录尝试失败。表存储时间戳,用户名,IP地址和浏览器类型。检测到错误的登录信息后,cfquery将根据用户名或IP地址从失败的登录表中提取记录。如果有5次或更多次无效尝试,我将帐户设置为非活动状态。现在我想以某种方式设置计时器,从上次无效尝试该用户开始计数5分钟。然后帐户应将状态更改为活动状态。这是我到目前为止的代码:

<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>

帐户设置为非活动/锁定后,设置时间倒计时和更改标志状态的最佳方法是什么?我看到有些人推荐SQL Job,但我不确定作业应该多久运行以及如何创建该语句?如果有人可以提供一些例子,请告诉我。谢谢。

2 个答案:

答案 0 :(得分:5)

您可以做的是为checkUser查询添加条件:

<cfquery name="checkUser" datasource="#dsn#">
    SELECT UserName, Password, Salt, Active
      FROM Users u
     WHERE UserName = <cfqueryparam cfsqltype="cf_sql_varchar" value="#trim(FORM.username)#" maxlength="50">
      -- AND Active = 1
      AND NOT EXISTS ( SELECT 1 FROM FailedLogins fl
                        WHERE fl.LoginUN = u.UserName
                          AND DATEDIFF('ss', fl.LoginTime, CURRENT_TIMESTAMP) >= 300 )
</cfquery>

我使用了300秒而不是5分钟,因为我认为DATEDIFF()会返回int。如果这不是SQL Server的理想语法(我不经常使用它),我会提前道歉。

然后,如上所述,如果Active为零,则可以(假设密码正确)将其更新为1,并删除与该帐户关联的失败登录或以某种方式标记它们处于非活动状态,因此它们不再计入5次登录失败。

根据以下评论者的建议编辑查询:(顺便提一句好建议!)

<cfquery name="checkUser" datasource="#dsn#">
    SELECT UserName, Password, Salt, Active
      FROM Users u
     WHERE UserName = <cfqueryparam cfsqltype="cf_sql_varchar" value="#trim(FORM.username)#" maxlength="50">
      -- AND Active = 1
      AND NOT EXISTS ( SELECT 1 FROM FailedLogins fl
                        WHERE fl.LoginUN = u.UserName
                          AND fl.loginTime < DATEADD(second, -300, CURRENT_TIMESTAMP) )
</cfquery>

答案 1 :(得分:2)

我认为你可以更好地扭转你的逻辑。 不要使用值statusActive的列Inactive,而应考虑使用locked_until列时间。

最初,新用户的locked_until值为NULL(或0),表示未锁定。

如果有一系列登录失败,请将此设置为当前时间+ 5分钟。

对于此用户的所有操作,请检查当前时间是否为&gt; locked_until值。 如果没有,该帐户仍然处于停用状态(已锁定)。

编辑:我决定写出一些代码,因为我忘记了用户成功登录的帐号。请参阅下文;我不确定原始问题的语言是什么,但这个答案是伪蟒蛇。

假设我们有一个类似于以下的数据库表(忽略盐等...)

CREATE TABLE Users (
    UserName TEXT PRIMARY KEY,
    Password TEXT NOT NULL,
    LockUntil TIMESTAMP,
    FailedLogins INT DEFAULT 0
);

登录检查功能如下所示。 要点是:

  • 成功登录清除会将FailedLogins设置为0。
  • 锁定帐户时将FailedLogins设置为5(以及LockUntil)。
  • 新的失败登录,其中FailedLogins = 5是尝试新解锁的帐户。 (即帐户被隐式解锁,用户再次尝试)。
def try_login(username, password):
    row = execute("SELECT Password,LockUntil,FailedLogins FROM Users WHERE UserName=?", username);
    if row is None:
        print("Unknown username")
        return False

    if row.LockUntil is not None and current_time() < row.LockUntil:
        print("Account locked. Try again later.")
        return False

    if password == row.Password:
        print('Successful login')
        execute("UPDATE Users SET LockUntil=NULL, FailedLogins=0 WHERE UserName=?", username)
        return True

    if row.FailedLogins == 4:
        print("Too many failures; locking account for 5 mins")
        lock_until = current_time() + 300
        execute("UPDATE Users SET LockUntil=?,FailedLogins=5 WHERE UserName=?", lock_until, username)
        return False

    failures = row.FailedLogins + 1
    if failures == 6:
        # User had locked account, which is now unlocked again.
        # But they failed to login again, so this is failure 1.
        failures = 1
    execute("UPDATE Users SET FailedLogins=? WHERE UserName=?", failures, username)
    return False