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

发布于 2024-07-22 04:38:22 字数 439 浏览 7 评论 0原文

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

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

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

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

I'm working on a fairly large web site built in PHP that will potentially have a lot of users. I'm looking into a way to protect the login screen from automated attempts. I have already included a CAPTCHA check on the registration form, yet want to harden the site more.

There have been similar questions on StackOverflow that I know of, and I know I'm capable of implementing this myself from scratch (storing login attempts and their time in the db), yet I dislike that path:

  • Conceptually, I think this kind of logic belongs at the web server/infrastructure level, not the application level. I dislike having this logic and complexity in my application
  • I worry about performance, particularly at the database level.
  • I'm lazy, in a good way, by not wanting to build a common utility like this from scratch

Any advise is appreciated, I think that I'm particularly looking for some kind of Apache module that can do this. My platform is PHP5 (using CodeIgniter), Apache2, MySQL 5.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

分分钟 2024-07-29 04:38:22

更新:不要使用 sleep() 进行速率限制!这根本没有意义。 我手头没有更好的解决方案。


一个好的开始是在登录尝试失败后sleep(1); - 易于实现,几乎没有错误。

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

如果攻击者启动过多的连接来规避此问题,您将面临一种 DOS 攻击。 问题解决了(但现在你又遇到了另一个问题)。

您应该考虑一些事情:

  • 如果您仅根据每个 IP 锁定帐户,则专用网络可能会出现问题。
  • 如果您仅基于用户名锁定帐户,那么针对已知用户名的拒绝服务攻击可能会
  • 基于 IP/用户名(其中用户名是被攻击的)进行锁定,效果会更好

我的建议:
完全锁定是不可取的(DOS),因此更好的选择是:计算来自唯一 IP 的特定用户名的登录尝试。 您可以使用简单的表 failed_logins: IP/username/failed_attempts 来完成此操作,

如果登录失败,则等待 (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!'));
}
?>

免责声明:这可能在某些地区不起作用。 我听到的最后一件事是,在亚洲有一个完整的国家 NATed(而且,他们都懂功夫)。

update: do not use sleep() for rate limiting! this doesn't make sense at all. i don't have a better solution on hand.


a good start would be to just sleep(1); after a failed login attempt - easy to implement, almost bug-free.

1 second isn't much for a human (especially because login attempts by humans don't fail to often), but 1sec/try brute-force ... sloooow! dictionary attacks may be another problem, but it's in the same domain.

if the attacker starts too may connections to circumvent this, you deal with a kind of DOS-attack. problem solved (but now you've got another problem).

some stuff you should consider:

  • if you lock accounts soley on a per IP basis, there may be problems with private networks.
  • if you lock accounts soley on a username basis, denial-of-service attacks agains known usernames would be possible
  • locking on a IP/username basis (where username is the one attacked) could work better

my suggestion:
complete locking is not desireable (DOS), so a better alternative would be: count the login attempts for a certain username from a unique IP. you could do this with a simple table failed_logins: IP/username/failed_attempts

if the login fails, wait(failed_attempts); seconds. every xx minutes, run a cron script that decreases failed_logins:failed_attempts by one.

sorry, i can't provide a premade solution, but this should be trivial to implement.

okay, okay. here's the pseudocode:

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

disclaimer: this may not work in certain regions. last thing i heard was that in asia there's a whole country NATed (also, they all know kung-fu).

早茶月光 2024-07-29 04:38:22

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

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 等进行同样的操作...

A very dummy untested example, but I think, you will find here the main idea ).

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 ... ");
}

Sorry for the mistakes - I wrote it in some seconds ad didn't test...
The same you can do by IP, by nickname, by session_id etc...

娇纵 2024-07-29 04:38:22

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

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

Why don't you wait with "hardening" and "scaling" your app until you actually have that problem? Most likely scenario is that the app will never have "a lot of users". This sounds like premature optimization to me, something to avoid.

  • Once you get bots abusing, harden the signup. I'd actually remove the captcha until you start getting > 1000 signups/day.
  • Once you get performance problems, improve performance by fixing real bottlenecks.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文