更新:不要使用 sleep() 进行速率限制!这根本没有意义。我手头没有更好的解决方案。
一个好的开始是sleep(1);在一次失败的登录尝试之后 - 易于实施,几乎没有错误。
1 秒对人类来说并不算多(特别是因为人类的登录尝试不会经常失败),但是 1 秒/尝试蛮力......慢!字典攻击可能是另一个问题,但它在同一个域中。
如果攻击者启动了也可能连接来规避这一点,你处理的是一种 DOS 攻击。问题已解决(但现在您遇到了另一个问题)。
你应该考虑的一些事情:
- 如果您在每个 IP 的基础上单独锁定帐户,则专用网络可能会出现问题。
- 如果您仅根据用户名锁定帐户,则可能会针对已知用户名进行拒绝服务攻击
- 在 IP/用户名的基础上锁定(其中用户名是被攻击的人)可以更好地工作
我的建议:
完全锁定是不可取的(DOS),因此更好的选择是:计算来自唯一 IP 的某个用户名的登录尝试。你可以用一个简单的表来做到这一点failed_logins: IP/username/failed_attempts
如果登录失败,wait(failed_attempts);秒。每 xx 分钟,运行一个减一的 cron 脚本failed_logins:failed_attempts。
抱歉,我无法提供预制的解决方案,但这应该很容易实现。
好吧好吧。这是伪代码:
<?php
$login_success = tryToLogIn($username, $password);
if (!$login_success) {
// some kind of unique hash
$ipusr = getUserIP() . $username;
DB:update('INSERT INTO failed_logins (ip_usr, failed_attempts) VALUES (:ipusr, 1) ON DUPLICATE KEY UPDATE failed_logins SET failed_attempts = failed_attempts+1 WHERE ip_usr=:ipusr', array((':ipusr' => $ipusr));
$failed_attempts = DB:selectCell('SELECT failed_attempts WHERE ip_usr=:ipusr', array(':ipusr' => $ipusr));
sleep($failed_attempts);
redirect('/login', array('errorMessage' => 'login-fail! ur doin it rong!'));
}
?>
免责声明:这可能不适用于某些地区。我听到的最后一件事是,在亚洲有一个国家被 NAT(而且,他们都知道功夫)。