Как заблокировать учетную запись пользователя на какой-то период времени?
Мне интересно, как и как лучше всего заблокировать учетную запись пользователя после 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 ответа
Я думаю, вам повезет с изменением вашей логики. Вместо того, чтобы столбец status
со значениями Active
или же Inactive
рассмотреть вопрос о наличии столбца locked_until
время вместо
Первоначально значение locked_until для нового пользователя будет равно NULL (или 0), что означает, что оно не заблокировано.
При наличии серии неудачных входов установите текущее время + 5 минут.
Для всех действий для этого пользователя проверьте, является ли текущее время значением> 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
Что вы можете сделать, это добавить условие к 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>