我只是在阅读这篇文章 明确的指导,以形成基于网站的身份验证 关于防止迅速火登录尝试。

最好的实践#1:一个很短的时间延迟,增加数失败的尝试,如:

1失败的尝试=没有延迟
2失败的尝试=2秒的拖延
3次失败的尝试=4秒钟的延迟
4失败的尝试=8秒的拖延
5失败的尝试=16秒的延误
等等。

DoS攻击的这种方案将是非常不切实际的,但另一方面,潜在的破坏性,因为延迟的增加呈指数增长。

我很好奇我可能实现这样的事情我登录系统在PHP?

有帮助吗?

解决方案

您不能简单地防止DoS攻击通过链接节流到一个单一的IP或用户名。该死的,你甚至不能真正避免使用此方法速射登录尝试。

为什么? 因为攻击可以跨越绕过您的节流尝试着想多个IP地址和用户帐户。

我已经看到张贴在其他地方,理想,你应该跟踪所有整个网站的失败的登录尝试,并将其关联到一个时间戳,也许是:

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地址转换,并从一个无符号的整数

# 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表拉,因为它会的随时间的变化的基于用户的数量以及有多少人还记得(和类型)他们的密码。


> 10 failed attempts = 1 second
> 20 failed attempts = 2 seconds
> 30 failed attempts = reCaptcha

查询在每次失败的登录尝试找到登录失败的给定时间内的数表,比如15分:


SELECT COUNT(1) AS failed FROM failed_logins WHERE attempted > DATE_SUB(NOW(), INTERVAL 15 minute);

如果在给定的时间周期尝试的数目是在你的限制,无论是执行节流或强制所有用户的使用验证码(即验证码),直到失败的尝试在给定时间周期的数目小于所述阈值。

// 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;
            }
        }        
    }
}

在一定的阈值使用验证码将确保来自多方面的攻击将被停止和正常网站的用户将不会遇到合法的失败登录的显著延迟。

其他提示

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

如果您使用会话信息的最终用户(攻击者)可以强行调用新的会话,绕过您的战术,然后用无延迟重新登录。会议是落实,简单地存储在会话变量用户的最后已知的登录时间相当简单,匹配它的当前时间,并确保延迟已经足够长了。

如果您使用cookie,攻击者可以简单地拒绝饼干,一切的一切,这真的是不是可行。

如果您追踪IP地址,您需要登录尝试存储从IP地址不知何故,最好在数据库中。当用户试图登录,只需更新您的IP地址记录列表。你应该在一个合理的区间清除此表中,反倾销已经一段时间没有被激活的IP地址。该陷阱(总有一个陷阱),是有些用户可能最终会共享一个IP地址,并在边界条件的延迟可能影响用户不经意的。既然你跟踪失败的登录,只有登录失败,这应该不会造成太大的痛苦。

在登录过程需要减少其对于成功和失败登录速度。登录尝试本身应该不会比约1秒快。如果是,蛮力利用延迟知道,尝试失败,因为成功比失败更短。然后,更多的组合可以每秒进行评估。

需要每个机器同时登录尝试的数目由负载平衡器来限定。最后,你只需要跟踪,如果相同的用户名或密码重复使用一个以上的用户名/密码登录尝试。人类不能键入超过每minite约200单词的速度。所以,连续或同时的登录尝试每minite快于200个字是从一组机器。这些可以因此通过管道输送到黑名单安全的,因为它不是你的客户。每台主机黑名单时间不需要比约1秒以上。这不会带来不便人,但在串行或并行严重破坏用蛮力尝试是否

2×10 ^ 19个组合以每秒一个组合,并行地在4个十亿单独的IP地址上运行,将需要158年用尽作为搜索空间。到最后每个用户1天对于4-十亿攻击,你需要至少一个完全随机的字母数字密码,9位长。考虑在训练密码短语的用户至少13个地方长,1.7 * 10 ^ 20个的组合。

这延迟,将激励攻击者窃取您的密码哈希文件,而不是蛮力您的网站。使用批准,命名,散列技术。禁止互联网IP的全部人口一秒钟,将限制并行攻击的效果没有迪利人将不胜感激。最后,如果你的系统允许多于1000在一秒钟内失败的登录尝试没有一些应对禁系统,那么你的安全计划有更大的问题上下工夫。第一修复所有的该自动响应。

session_start();
$_SESSION['hit'] += 1; // Only Increase on Failed Attempts
$delays = array(1=>0, 2=>2, 3=>4, 4=>8, 5=>16); // Array of # of Attempts => Secs

sleep($delays[$_SESSION['hit']]); // Sleep for that Duration.

或:由Cyro提示:

sleep(2 ^ (intval($_SESSION['hit']) - 1));

这是一个有点粗糙,但基本成分的存在。如果刷新此页面,每次刷新延迟将变长。

您也可以保持数在数据库中,在那里你检查失败的尝试通过IP的数量。通过使用其基于IP,并保持在你身边的数据,防止用户能够明确自己的cookie来阻止延迟。

基本上,开头代码将是:

$count = get_attempts(); // Get the Number of Attempts

sleep(2 ^ (intval($count) - 1));

function get_attempts()
{
    $result = mysql_query("SELECT FROM TABLE WHERE IP=\"".$_SERVER['REMOTE_ADDR']."\"");
    if(mysql_num_rows($result) > 0)
    {
        $array = mysql_fetch_assoc($array);
        return $array['Hits'];
    }
    else
    {
        return 0;
    }
}

存储失败在由IP数据库尝试。 (因为你有一个登录系统,我想你也知道如何做到这一点。)

显然,会议是一个诱人的方法,但真的有人专门可以很容易地意识到,他们可以简单地删除自己的会话cookie对失败的尝试,以规避整个油门。

在尝试登录,获取多少近期(比如最后15分钟)的登录尝试有,和最新尝试的时间。

$failed_attempts = 3; // for example
$latest_attempt = 1263874972; // again, for example
$delay_in_seconds = pow(2, $failed_attempts); // that's 2 to the $failed_attempts power
$remaining_delay = time() - $latest_attempt - $delay_in_seconds;
if($remaining_delay > 0) {
    echo "Wait $remaining_delay more seconds, silly!";
}

您可以使用会话。每当用户无法登录,可以增加存储的尝试次数值。您可以尝试的数量计算所需的延迟,也可以设置允许用户在会议上再次尝试,以及实际的时间。

一个更可靠的方法将是在数据库中存储的企图和新尝试时间针对该特定IPADDRESS。

恕我直言,对DoS攻击的防御是更好的在Web服务器级别(甚至在网络硬件或可能)处理,而不是在你的PHP代码。

我通常创建登录历史和登录尝试的表。企图表将登录的用户名,密码,IP地址等查询对阵表,看看你是否需要延迟。我建议用于在给定时间(例如1小时)的企图大于20完全阻断。

按上面的讨论、会议、饼干和IP地址不是有效的-所有的可以通过操纵的攻击。

如果您想要防止暴力攻击,那么唯一实际的解决办法是以基数的尝试对用户名提供的,但是请注意,这使攻击者DOS该网站通过阻止有效的用户登录。

例如

$valid=check_auth($_POST['USERNAME'],$_POST['PASSWD']);
$delay=get_delay($_POST['USERNAME'],$valid);

if (!$valid) {
   header("Location: login.php");
   exit;
}
...
function get_delay($username,$authenticated)
{
    $loginfile=SOME_BASE_DIR . md5($username);
    if (@filemtime($loginfile)<time()-8600) {
       // last login was never or over a day ago
       return 0;
    }
    $attempts=(integer)file_get_contents($loginfile);
    $delay=$attempts ? pow(2,$attempts) : 0;
    $next_value=$authenticated ? 0 : $attempts + 1;
    file_put_contents($loginfile, $next_value);
    sleep($delay); // NB this is done regardless if passwd valid
    // you might want to put in your own garbage collection here
 }

注意,如书写的,这个过程中的泄漏信息的安全--即这将有可能为个人攻击该系统的时候看到一个用户登录(时间响应对于攻击者试图将降为0)。你可能也会为你的算法,以便延迟的计算是基于以前的延迟和上的时间戳文件。

禾田

C.

缓存或基于会话的方法是在这种情况下,当然也没用。该应用程序必须检查以前登录尝试的IP地址或时间戳(或两者)。

这是IP检查可以绕过如果攻击者有一个以上的IP,开始他/她从要求,如果多个用户来自同一个IP连接到你的服务器可能会很麻烦。在后一种情况下,有人失败登录几次会阻止大家谁分享从与该用户名进行的一段时间内登录同一IP。

有一个时间戳检查有同样的问题如上:每个人都可以防止从每个人在特定帐户登录其他只是试图多次。使用验证码,而不是漫长的等待最后一次尝试可能是一个很好的解决方法。

在唯一的额外的东西登录系统应防止都在尝试检查功能的竞争条件。例如,在下面的伪代码

$time = get_latest_attempt_timestamp($username);
$attempts = get_latest_attempt_number($username);

if (is_valid_request($time, $attempts)) {
    do_login($username, $password);
} else {
    increment_attempt_number($username);
    display_error($attempts);
}

如果攻击者发送的同时的请求到登录页面,会发生什么?也许所有的请求将在同一优先级运行,并有机会,没有人要求获取到increment_attempt_number指令其他都是过去二号线之前。因此,每个请求得到相同的$时间和$尝试值并执行。防止这种安全问题可能很难对复杂的应用程序,涉及锁定和解锁一些表的数据库/行,当然拖慢应用。

简短的回答是:不这样做。你不会保护自己免受强迫的,你甚至可以让你的情况更糟。

没有提出解决办法的工作。如果您使用的知识产权作为任何参数的限制,攻击者只是将跨度的攻击,在巨大的数IPs。如果您使用的会议(cookie),攻击者只是将降大任饼干。所有你能想到的是,那绝对没有强迫使攻击者不能克服的。

有一件事,虽然你只要依靠用户名,试图登陆。因此,不是在寻找所有其他参数,追踪经常用户试图在节流。但是,一个攻击者想要伤害你。如果他认识到这一点,他将只是还蛮武力的用户名。

这将导致在几乎所有用户在被限制到最大值,当他们试图登陆。你的网站将是无用的。攻击:成功。

你可以延迟的密码检查中的大约200毫秒-该网站的用户将几乎不注意到这一点。但蛮压入器求。(再次他可以跨越IPs)然而,没有什么所有这一切将保护你免受强迫或DDoS-你不能通过程序.

只有这样,才能做到这是使用基础设施。

你应该使用bcrypt而不是MD5或SHA-x哈希您的密码,这将使解你的密码很难,如果有人偷了你的数据库(因为我猜你们在公共管理的主机)

对不起让你失望的,但是所有的解决方案,这里有一个弱点,是没有办法来克服他们内部的后端的逻辑。

cballuo提供了极好的答案。我只是想通过提供支持的mysqli的更新版本返回的青睐。我稍微改变了表/字段列在sql语句等小东西,但它应该帮助别人寻找的mysqli等价的。

function get_multiple_rows($result) {
  $rows = array();
  while($row = $result->fetch_assoc()) {
    $rows[] = $row;
  }
  return $rows;
}

$throttle = array(10 => 1, 20 => 2, 30 => 5);

$query = "SELECT MAX(time) AS attempted FROM failed_logins";    

if ($result = $mysqli->query($query)) {

    $rows = get_multiple_rows($result);

$result->free();

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

$query = "SELECT COUNT(1) AS failed FROM failed_logins WHERE time > DATE_SUB(NOW(), 
INTERVAL 15 minute)";   

if ($result = $mysqli->query($query)) {

$rows = get_multiple_rows($result);

$result->free();

    $failed_attempts = (int) $rows[0]['failed'];

    krsort($throttle);
    foreach ($throttle as $attempts => $delay) {
        if ($failed_attempts > $attempts) {
                echo $failed_attempts;
                $remaining_delay = (time() - $latest_attempt) - $delay;

                if ($remaining_delay < 0) {
                echo 'You must wait ' . abs($remaining_delay) . ' seconds before your next login attempt';
                }                

            break;
        }
     }        
  }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top