首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何在PHP中限制用户登录尝试

如何在PHP中限制用户登录尝试
EN

Stack Overflow用户
提问于 2010-01-19 11:42:08
回答 12查看 37.6K关注 0票数 57

我刚刚读了一篇关于防止快速登录尝试的帖子The definitive guide to form-based website authentication

最佳实践#1:随着失败尝试次数的增加而增加的短时间延迟,例如:

1次尝试失败=无延迟

2次失败尝试=2秒延迟

3次失败的尝试=4秒延迟

4次失败尝试=8秒延迟

5次失败的尝试= 16秒延迟

等。

DoS攻击此方案是非常不切实际的,但另一方面,由于延迟呈指数级增加,因此具有潜在的破坏性。

我很好奇怎样才能在我的登录系统中实现这样的东西?

EN

回答 12

Stack Overflow用户

回答已采纳

发布于 2010-01-19 20:16:52

您不能通过将限制链接到单个IP或用户名来简单地防止DoS攻击。见鬼,你甚至不能用这种方法来阻止快速登录尝试。

为什么?,因为为了绕过您的限制尝试,攻击可以跨越多个IP和用户帐户。

我在其他地方看到过这样的帖子,理想情况下,您应该跟踪整个站点上所有失败的登录尝试,并将它们与时间戳相关联,例如:

代码语言:javascript
复制
CREATE TABLE failed_logins (
    id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(16) NOT NULL,
    ip_address INT(11) UNSIGNED NOT NULL,
    attempted DATETIME NOT NULL,
    INDEX `attempted_idx` (`attempted`)
) engine=InnoDB charset=UTF8;

关于ip_address字段的快速说明:您可以分别使用INET_ATON()和INET_NTOA()来存储数据和检索数据,这实质上等同于将ip地址转换为无符号整数或将其转换为无符号整数。

代码语言:javascript
复制
# example of insertion
INSERT INTO failed_logins SET username = 'example', ip_address = INET_ATON('192.168.0.1'), attempted = CURRENT_TIMESTAMP;
# example of selection
SELECT id, username, INET_NTOA(ip_address) AS ip_address, attempted;

根据给定时间量(在本例中为15分钟)内失败登录的总数来决定特定的延迟阈值。您应该基于从failed_logins表中提取的统计数据,因为它将根据用户的数量以及其中有多少用户可以回忆(并键入)其密码而随着时间的推移而变化。

代码语言:javascript
复制
> 10 failed attempts = 1 second
> 20 failed attempts = 2 seconds
> 30 failed attempts = reCaptcha

在每次失败的登录尝试时查询表,以查找给定时间段(例如15分钟)内失败的登录次数:

代码语言:javascript
复制
SELECT COUNT(1) AS failed FROM failed_logins WHERE attempted > DATE_SUB(NOW(), INTERVAL 15 minute);

如果给定时间段内的尝试次数超过您的限制,请强制限制或强制所有用户使用验证码(即reCaptcha),直到给定时间段内失败的尝试次数小于阈值。

代码语言:javascript
复制
// array of throttling
$throttle = array(10 => 1, 20 => 2, 30 => 'recaptcha');

// retrieve the latest failed login attempts
$sql = 'SELECT MAX(attempted) AS attempted FROM failed_logins';
$result = mysql_query($sql);
if (mysql_affected_rows($result) > 0) {
    $row = mysql_fetch_assoc($result);

    $latest_attempt = (int) date('U', strtotime($row['attempted']));

    // get the number of failed attempts
    $sql = 'SELECT COUNT(1) AS failed FROM failed_logins WHERE attempted > DATE_SUB(NOW(), INTERVAL 15 minute)';
    $result = mysql_query($sql);
    if (mysql_affected_rows($result) > 0) {
        // get the returned row
        $row = mysql_fetch_assoc($result);
        $failed_attempts = (int) $row['failed'];

        // assume the number of failed attempts was stored in $failed_attempts
        krsort($throttle);
        foreach ($throttle as $attempts => $delay) {
            if ($failed_attempts > $attempts) {
                // we need to throttle based on delay
                if (is_numeric($delay)) {
                    $remaining_delay = time() - $latest_attempt - $delay;
                    // output remaining delay
                    echo 'You must wait ' . $remaining_delay . ' seconds before your next login attempt';
                } else {
                    // code to display recaptcha on login form goes here
                }
                break;
            }
        }        
    }
}

在特定阈值下使用reCaptcha将确保阻止来自多个战线的攻击,并且正常站点用户不会因为合法的失败登录尝试而经历显着的延迟。

票数 83
EN

Stack Overflow用户

发布于 2010-01-19 11:47:36

您有三种基本方法:存储会话信息、存储cookie信息或存储IP信息。

如果您使用会话信息,最终用户(攻击者)可能会强行调用新会话,绕过您的策略,然后毫无延迟地再次登录。会话的实现非常简单,只需将用户的最后已知登录时间存储在会话变量中,将其与当前时间进行匹配,并确保延迟足够长。

如果你使用cookies,攻击者可以简单地拒绝cookies,总而言之,这确实是不可行的。

如果您跟踪IP地址,您将需要以某种方式存储来自IP地址的登录尝试,最好是在数据库中。当用户尝试登录时,只需更新您记录的IP列表即可。您应该以合理的时间间隔清除此表,转储一段时间内不活动的IP地址。陷阱(总是有陷阱)是,一些用户可能最终共享一个IP地址,并且在边界条件下,您的延迟可能会无意中影响用户。由于您正在跟踪失败的登录,并且只跟踪失败的登录,这应该不会造成太大的痛苦。

票数 6
EN

Stack Overflow用户

发布于 2012-06-30 11:55:52

简短的答案是:不要这样做。你不会保护自己免受暴力的伤害,你甚至会让你的情况变得更糟。

所有提出的解决方案都不会奏效。如果您使用IP作为任何参数进行限制,攻击者将只会将攻击扩展到大量IP。如果你使用会话(Cookie),攻击者就会丢弃任何cookie。总而言之,你能想到的就是,绝对没有什么是蛮力攻击者不能克服的。

但是,有一件事-您只需要依赖尝试登录的用户名。因此,不查看所有其他参数,您可以跟踪用户尝试登录和限制的频率。但是攻击者想要伤害你。如果他认识到这一点,他也会暴力破解用户名。

这将导致几乎所有用户在尝试登录时都被限制为您的最大值。你的网站将变得毫无用处。攻击者:成功。

你可以将密码检查延迟大约200ms -网站用户几乎不会注意到这一点。但是一个蛮横的人会的。(同样,他可以跨越多个IP)然而,所有这些都不会保护您免受暴力攻击或DDoS攻击--因为您在编程上做不到这一点。

做到这一点的唯一方法是使用基础设施。

您应该使用bcrypt而不是MD5或SHA-x来散列您的密码,如果有人窃取您的数据库,这将使解密您的密码变得更加困难(因为我猜您是在共享的或托管的主机上)

很抱歉让您失望了,但是这里的所有解决方案都有一个弱点,并且没有办法在后端逻辑中克服它们。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2090910

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档