8

我正在开发一个用 PHP 构建的相当大的网站,该网站可能会有很多用户。我正在寻找一种方法来保护登录屏幕免受自动尝试。我已经在注册表单上添加了验证码检查,但还想进一步强化网站。

我知道在 StackOverflow 上有类似的问题,而且我知道我能够自己从头开始实现(将登录尝试及其时间存储在数据库中),但我不喜欢这条路:

  • 从概念上讲,我认为这种逻辑属于 Web 服务器/基础架构级别,而不是应用程序级别。我不喜欢在我的应用程序中有这种逻辑和复杂性
  • 我担心性能,尤其是在数据库级别。
  • 我很懒,不想从头开始构建这样的通用实用程序

任何建议表示赞赏,我认为我特别在寻找某种可以做到这一点的 Apache 模块。我的平台是 PHP5(使用 CodeIgniter)、Apache2、MySQL 5。

4

3 回答 3

18

更新:不要使用 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(而且,他们都知道功夫)。

于 2009-05-20T08:04:28.203 回答
2

一个非常愚蠢的未经测试的例子,但我认为,你会在这里找到主要思想)。

if ($unlockTime && (time() > $unlockTime))
{
    query("UPDATE users SET login_attempts = 0, unlocktime = 0 ... ");
}
else
{
   die ('Your account is temporary locked. Reason: too much wrong login attempts.');
}
if (!$logged_in)
{
    $loginAttempts++;
    $unlocktime = 0;
    if ($loginAttempts > MAX_LOGIN_ATTEMPTS) 
    {
        $unlockTime = time() + LOCK_TIMEOUT;
    }
    query("UPDATE users SET login_attempts = $loginAttempts, unlocktime = $unlocktime ... ");
}

对错误感到抱歉 - 我在几秒钟内写了它,广告没有测试......你可以通过 IP、昵称、session_id 等来做同样的事情......

于 2009-05-20T09:03:16.400 回答
-3

为什么不等待“强化”和“缩放”您的应用程序,直到您真正遇到这个问题?最可能的情况是应用程序永远不会有“很多用户”。这对我来说听起来像是过早的优化,需要避免。

  • 一旦你得到机器人滥用,加强注册。我实际上会删除验证码,直到您开始每天获得 > 1000 次注册。
  • 一旦遇到性能问题,通过修复真正的瓶颈来提高性能。
于 2009-05-20T09:48:32.703 回答