Как я могу отрегулировать пользовательские попытки входа в систему в PHP

Я просто читал это сообщение полное руководство к основанной на форме аутентификации веб-сайта на Предотвращении Скоропалительных Попыток Входа в систему.

Лучшая практика № 1: кратковременная задержка, которая увеличивается с количеством неудачных попыток, как:

1 неудачная попытка = никакая задержка
2 неудачных попытки = задержка 2 секунд
3 неудачных попытки = задержка 4 секунд
4 неудачных попытки = задержка 8 секунд
5 неудачных попыток = задержка 16 секунд
и т.д.

DoS нападая на эту схему был бы очень непрактичным, но с другой стороны, потенциально разрушительным, так как задержка увеличивается экспоненциально.

Мне любопытно, как я мог реализовать что-то вроде этого для своей системы входа в систему в PHP?

53
задан Community 23 May 2017 в 02:26
поделиться

9 ответов

Вы не можете просто нельзя просто Предотвратите атаки DOS, привязывая дросселю до одного IP или имени пользователя. Черт, вы даже не можете предотвратить попытки входа в систему быстрого пожара, используя этот метод.

Почему? потому что атака может охватывать несколько IPS и пользовательских учетных записей ради в обход ваших попыток дросселирования.

Я видел, как в другом месте, что в идеале вы должны отслеживать все неудачные попытки входа в систему по всему сайту и связываете их на временную метку, возможно:

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-адреса в и из unsigned целого числа.

# 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);

Если количество попыток в течение данного периода времени превышает ваш лимит, либо принудительно дросселирование или заставить всех пользователей использовать капчу (т.е. recaptcha) до тех пор, пока число неудачных попыток в течение заданного периода времени не меньше порогового значения.

// 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 на определенном пороге гарантирует, что атака нескольких фронтов будет остановлена, и обычные пользователи сайта не будут испытывать значительную задержку для законных неудачных попыток входа в систему.

76
ответ дан 7 November 2019 в 08:40
поделиться
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 и сохраняя данные на вашей стороне, вы не даете пользователю быть возможность очистить свои куки, чтобы остановить задержку.

В основном начальный код:

$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;
    }
}
-121--1018872-

Можно использовать сеансы. Каждый раз, когда пользователю не удается войти в систему, вы увеличиваете значение, сохраняющее количество попыток. Можно определить требуемую задержку по количеству попыток или установить фактическое время, в течение которого пользователю будет разрешено повторить попытку в сеансе.

Более надежным способом было бы сохранение попыток и времени новых попыток в базе данных для этого конкретного ipadress.

-121--1018874-

Я обычно создаю историю входа в систему и таблицы попыток входа в систему. Таблица попыток регистрирует имя пользователя, пароль, IP-адрес и т.д. Выполните запрос к таблице, чтобы узнать, требуется ли задержка. Я бы рекомендовал полностью блокировать попытки больше 20 за заданное время (например, час).

1
ответ дан 7 November 2019 в 08:40
поделиться

Магазин не удается попытки в базе данных по 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!";
}
3
ответ дан 7 November 2019 в 08:40
поделиться

Можно использовать сеансы. Каждый раз, когда пользователю не удается войти в систему, вы увеличиваете значение, сохраняющее количество попыток. Можно определить требуемую задержку по количеству попыток или установить фактическое время, в течение которого пользователю будет разрешено повторить попытку в сеансе.

Более надежным способом было бы сохранение попыток и времени новых попыток в базе данных для этого конкретного ipadress.

-121--1018874-

Сохранение неудачных попыток в базе данных по 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!";
}
-121--1018873-

У вас есть три основных подхода: хранить информацию о сеансе, хранить информацию о cookie или хранить IP-информацию.

Если вы используете информацию о сеансе, конечный пользователь (злоумышленник) может принудительно вызвать новые сеансы, обойти вашу тактику и снова войти в систему без задержки. Сеансы довольно просты в реализации, просто храните последнее известное время входа пользователя в систему в переменной сеанса, сопоставляйте его с текущим временем и убедитесь, что задержка была достаточно длительной.

Если вы используете файлы cookie, злоумышленник может просто отклонить файлы cookie, в целом, это на самом деле не является чем-то жизнеспособным.

Если вы отслеживаете IP-адреса, вам потребуется сохранить попытки входа с IP-адреса каким-либо образом, предпочтительно в базе данных. Когда пользователь пытается войти в систему, просто обновите записанный список IP-адресов. Необходимо очистить эту таблицу с разумным интервалом, сбросив IP-адреса, которые не были активны в течение некоторого времени. Ловушка (всегда есть ловушка) заключается в том, что некоторые пользователи могут в конечном итоге совместно использовать IP-адрес, а в граничных условиях задержки могут непреднамеренно повлиять на пользователей. Так как вы отслеживаете неудачные логины, и только неудачные логины, это не должно вызывать слишком много боли.

5
ответ дан 7 November 2019 в 08:40
поделиться

Вы можете использовать сеансы. В любое время пользователь не проходит вход, вы увеличиваете ценность, хранящее количество попыток. Вы можете понять необходимую задержку от количества попыток, или вы можете установить фактическое время, которое пользователю разрешено еще раз в сеансе.

Более надежный метод будет хранить попытки и New-time в базе данных для этой конкретной iPaddress.

2
ответ дан 7 November 2019 в 08:40
поделиться

Согласно обсуждению выше, сеансы, печенье и 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.

1
ответ дан 7 November 2019 в 08:40
поделиться

ИМХО, защита от атак DOS лучше рассмотрена на уровне веб-сервера (или, может быть, даже в сетевом оборудовании), а не в вашем PHP-коде.

2
ответ дан 7 November 2019 в 08:40
поделиться
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;
    }
}
3
ответ дан 7 November 2019 в 08:40
поделиться

Cookies или сеансовые методы, конечно же, бесполезны в этом случае. Приложение должно проверять 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 раньше, чем остальные пройдут 2-ю строку. Таким образом, каждый запрос получает одинаковое значение $time и $attempts и выполняется. Предотвращение такого рода проблем с безопасностью может быть затруднено для сложных приложений и включает блокировку и разблокировку некоторых таблиц/строк базы данных, конечно же, замедляя работу приложения.

1
ответ дан 7 November 2019 в 08:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: