如何在多次尝试后延迟登录尝试(PHP)

如何在多次尝试后延迟登录尝试(PHP),php,login-attempts,Php,Login Attempts,我正在开发一个用PHP构建的相当大的网站,它可能会有很多用户。我正在寻找一种方法来保护登录屏幕不受自动尝试的影响。我已经包括了一个验证码检查的登记表,但想硬化的网站更多 据我所知,在StackOverflow上也有类似的问题,我知道我可以自己从头开始实现(将登录尝试及其时间存储在数据库中),但我不喜欢这种方式: 从概念上讲,我认为这种逻辑属于web服务器/基础架构级别,而不是应用程序级别。我不喜欢在我的应用程序中有这种逻辑和复杂性 我担心性能,特别是在数据库级别 我很懒惰,因为我不想从头开始构

我正在开发一个用PHP构建的相当大的网站,它可能会有很多用户。我正在寻找一种方法来保护登录屏幕不受自动尝试的影响。我已经包括了一个验证码检查的登记表,但想硬化的网站更多

据我所知,在StackOverflow上也有类似的问题,我知道我可以自己从头开始实现(将登录尝试及其时间存储在数据库中),但我不喜欢这种方式:

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

任何建议都是值得赞赏的,我想我特别想寻找某种Apache模块来实现这一点。我的平台是PHP5(使用CodeIgniter)、Apache2、MySQL 5。

更新:不要使用sleep()进行速率限制这毫无意义。我手头没有更好的解决办法


一个好的开始就是睡眠(1)登录尝试失败后-易于实现,几乎没有bug

1秒对人类来说并不算多(特别是因为人类的登录尝试并不经常失败),但1秒/尝试暴力。。。斯洛!字典攻击可能是另一个问题,但它在同一个领域

如果攻击者启动太多连接来绕过此漏洞,则您将处理一种DOS攻击。问题解决了(但现在你又遇到了另一个问题)

你应该考虑以下几点:

  • 如果您在每个IP的基础上单独锁定帐户,则专用网络可能会出现问题
  • 如果您仅基于用户名锁定帐户,则可能会对已知用户名进行拒绝服务攻击
  • 基于IP/用户名的锁定(其中用户名是被攻击的用户名)可以更好地工作
我的建议: 完全锁定是不可取的(DOS),因此更好的选择是:从唯一IP计算某个用户名的登录尝试次数。您可以通过一个简单的表
failed\u logins:IP/username/failed\u truments

如果登录失败,
wait(尝试失败)秒。每xx分钟运行一个cron脚本,将
失败的\u登录:失败的\u尝试减少一次

很抱歉,我无法提供预先制作的解决方案,但这应该很容易实现

好,好。以下是伪代码:

<?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!'));
}
?>

这是一个非常虚假的未经测试的示例,但我认为,您可以在这里找到主要思想)

很抱歉,我在几秒钟内就写好了,广告没有测试。。。 同样,你可以通过IP、昵称、会话id等来实现这一点。

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

  • 一旦你被机器人滥用,加强注册。我实际上会删除验证码,直到你开始获得每天超过1000个注册
  • 一旦出现性能问题,通过修复真正的瓶颈来提高性能

我同意这是一个很好且简单的解决方案,但它仍然需要数据库交互。如果我只是在失败的登录检测和重新呈现登录屏幕之间添加1秒延迟,会怎么样?这将迫使机器人和人类不得不等待延迟。对于人类来说,这并不多,很少有人会做错误的登录,而对于机器人来说,这相当长。你觉得呢?那没什么帮助,因为我可以处理N个并发请求。1秒的阻塞发生在每个请求的基础上,因此N个并发请求在1秒多一点后完成(假定服务器可以处理负载)。因此,原则上,我可以启动10000个请求,并在几秒钟后完成它们,因为它们不是串行的。此外,每天有>80000秒。80.000对于暴力来说不算多,但对于字典攻击来说可能就足够了。答案不错,但我完全不同意“因为人类的登录尝试通常不会失败”。。。它们总是失败,比您想象的要多请避免睡眠,这将使攻击者更容易伤害您的服务,如果您睡眠5秒钟,所有脚本甚至对其他用户都将睡眠。睡眠将影响脚本本身,而不管当前启动睡眠的请求是什么。我已经测试过了。虽然你在概念上可能是对的,但你不知道我项目的背景,也不能断定它可能永远不会遇到这些问题。我认为这些保护最好的做法,无论大小的网站。如果我不防止登录尝试,密码可能会被盗。如何防范这种情况还为时过早?CAPTCHA也是如此。我正在使用我自己定制的博客软件,非常适合我。我只有300个读者,但仍然有机器人用垃圾评论攻击它。在我的经验中,验证码是必备的。为什么要在餐具抽屉里放儿童保护卡?为什么不等到孩子开始玩刀呢?这很可怕,原因很明显。
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 ... ");
}