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

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

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

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

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

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


person JasonDavis    schedule 19.01.2010    source источник
comment
Просто убедитесь, что нет арифметического переполнения, иначе вы можете получить отрицательную задержку.   -  person Hamish Grubijan    schedule 19.01.2010
comment
Не делайте задержки, просто полностью заблокируйте после x попыток. Отправлять 404, когда бот пытается войти в систему. Нет причин усложнять настройку задержки. Кроме того, не исключено, что человек потерпит неудачу 3 раза (и получит долгую задержку)   -  person sestocker    schedule 19.01.2010
comment
@sestocker На самом деле, я бы порекомендовал 418 I'm a teapot вместо 404 здесь. en.wikipedia.org/wiki/Http_status_codes; o)   -  person deceze♦    schedule 19.01.2010
comment
@jasondavis, на странице википедии написано, что это была первая апрельская шутка для дураков.   -  person sestocker    schedule 19.01.2010
comment
Боты @sestocker не соблюдают правила - так зачем нам сообщения об ошибках?   -  person Xeoncross    schedule 21.01.2010
comment
Почему вы спрашиваете о DoS-атаке, когда ваш вопрос касается безопасности входа в систему? Это две разные проблемы, и предотвращение DoS-атаки лучше всего решать на уровне сервера, как указано в одном из ответов.   -  person calvinlough    schedule 21.01.2010
comment
@sestocker: вы не хотите блокировать вход по многим причинам; DoS - одна причина, практичность - другая (если у вашего сайта миллионы учетных записей, вы не хотите повторно активировать своих пользователей вручную). Кроме того, 4 или 8 секунд - это не «долгая» задержка для человека, но это серьезная неприятность при атаке методом грубой силы. Вам, вероятно, следует ограничить задержку примерно 15 или 30 минутами и предоставить «прямую линию» на основе IP-адреса или файлов cookie, чтобы реальный пользователь не мог быть заблокирован повторными неудачными попытками. Подробности читайте в связанном сообщении.   -  person Jens Roland    schedule 13.07.2010
comment
4 или 8 секунд - это большая задержка для человека, который забыл свой пароль и хочет проверить дюжину вариантов (это был! Или @ в конце?). Посмотрите, что делает андроид с блокировкой экрана: 5 попыток, задержка, 5 попыток, задержка ... это более гуманный подход.   -  person Kos    schedule 13.09.2012


Ответы (12)


Вы не можете просто предотвратить 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);

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

person Corey Ballou    schedule 19.01.2010
comment
Это хороший момент, на самом деле я думал об этом, так как я видел программное обеспечение, которое может пытаться войти в систему на myspace с файлами электронной почты / паролей из 100000 логинов и другим паролем IP-адреса для использования, тогда он мог бы каким-то образом чередовать IP для каждого запроса так что это остановит подобные вещи, я думаю - person JasonDavis; 19.01.2010
comment
Стоит отметить, что время дросселирования должно быть достаточно низким, чтобы не раздражать обычных пользователей, но достаточно длинным, чтобы боты не запускали повторно запросы cURL. Пользователь даже не заметит двухсекундной задержки, поскольку его следующая попытка входа в систему, вероятно, превысит 2 секунды с момента предыдущей попытки. На бота, с другой стороны, сильно повлияет ожидание 2 секунды перед следующей попыткой. Скриптовые дети, скорее всего, уйдут в другое место, поскольку небольшая задержка значительно уменьшит количество общих запросов, которые они могут сделать. - person Corey Ballou; 19.01.2010
comment
Мне нравится эта идея. Возможно, вы могли бы взглянуть на этот пост: stackoverflow.com/questions/479233/ В нем обсуждается точно такая же проблема (распределенная грубая сила), и было бы неплохо, если бы вы также могли подробно опубликовать свою идею там - person Jens Roland; 13.07.2010
comment
Я думаю, что $ left_delay = time () - $ latest_attempt - $ delay; неправильно. Разве это не должно быть вместо этого ?: $ unknown_delay = $ delay - (time () - $ latest_attempt); - person Curt; 21.02.2014
comment
Я создал класс на основе вашей концепции, изложенной в этом комментарии, с некоторыми улучшениями под названием BruteForceBlocker для всех, кто хочет его использовать github .com / ejfrancis / BruteForceBlocker. - person ejfrancis; 15.06.2014
comment
У меня вопрос. Будет ли нормально, если я заблокирую IP-адрес имени пользователя? Например, если IP-адрес И имя пользователя были предприняты 10 раз, заблокируйте IP-адрес от попытки использовать это имя пользователя. Но реальный пользователь может нормально войти в систему со своим именем пользователя, потому что их IP-адрес отличается. Этот метод в порядке, или мне следует интегрировать его с приведенным выше ответом? - person Martynogea; 25.10.2014
comment
Мне любопытно, зачем нужен столбец id в таблице failed_logins? Предполагая, что вы регулярно очищаете таблицу. Но, может быть, есть еще одна причина? Кстати, спасибо за отличный ответ. - person Kosta Kontos; 23.03.2016

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

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

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

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

person Mark Elliot    schedule 19.01.2010
comment
IP-адреса не являются хорошим решением: 1) они часто используются совместно 2) легко продолжать менять адрес с помощью TOR - person symcbean; 19.01.2010
comment
@symcbean Я рассмотрел несколько решений, любая комбинация которых помешает некоторым злоумышленникам, волшебного решения нет. Совместное использование IP-адресов не является проблемой, о чем я говорю в своем ответе; то, что кто-то может изменить это с помощью TOR, кажется менее вероятным, чем кто-то принудительно запускает новые сеансы. Есть ли 4-й вариант, который я пропустил? - person Mark Elliot; 19.01.2010
comment
@symcbean Если используется TOR, обычно происходит неявное регулирование через накладные расходы через несколько задействованных уровней. По своему замыслу TOR обязательно усложнит ситуацию для атаки методом грубой силы. - person humanityANDpeace; 23.03.2018

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

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

Однако есть одна вещь - вы просто полагаетесь на имя пользователя, который пытался войти в систему. Таким образом, не глядя на все другие параметры, вы отслеживаете, как часто пользователь пытался войти в систему и задросселировать. Но злоумышленник хочет причинить вам вред. Если он распознает это, он просто также будет перебирать имена пользователей.

Это приведет к тому, что почти все ваши пользователи будут ограничены вашим максимальным значением при попытке входа в систему. Ваш веб-сайт будет бесполезен. Атакующий: успех.

Вы можете отложить проверку пароля в целом примерно на 200 мс - пользователь сайта этого почти не заметит. Но брутфорс будет. (Опять же, он может охватывать IP-адреса) Однако ничто из всего этого не защитит вас от брутфорса или DDoS - как вы не можете программно.

Единственный способ сделать это - использовать инфраструктуру.

Вы должны использовать bcrypt вместо MD5 или SHA-x для хеширования ваших паролей, это значительно усложнит расшифровку ваших паролей, если кто-то украдет вашу базу данных (потому что я предполагаю, что вы находитесь на общем или управляемом хосте)

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

person nico gawenda    schedule 30.06.2012

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

Количество одновременных попыток входа на компьютер должно быть ограничено балансировщиком нагрузки. Наконец, вам просто нужно отслеживать, используется ли один и тот же пользователь или пароль повторно при более чем одной попытке входа пользователя / пароля. Люди не могут печатать быстрее 200 слов в минуту. Таким образом, последовательные или одновременные попытки входа в систему со скоростью, превышающей 200 слов в минуту, поступают от набора машин. Таким образом, их можно безопасно поместить в черный список, поскольку это не ваш клиент. Время черного списка на хост не должно превышать 1 секунду. Это никогда не доставит неудобств человеку, но нанесет ущерб попыткам грубой силы, последовательным или параллельным.

2 * 10 ^ 19 комбинаций при одной комбинации в секунду, выполняемых параллельно на 4 миллиардах отдельных IP-адресов, займет 158 лет, чтобы исчерпать пространство поиска. Чтобы продержаться один день на пользователя против 4 миллиардов злоумышленников, вам понадобится полностью случайный буквенно-цифровой пароль как минимум из 9 знаков. Рассмотрите возможность обучения пользователей парольным фразам длиной не менее 13 знаков, 1,7 * 10 ^ 20 комбинаций.

Эта задержка побудит злоумышленника украсть хэш-файл вашего пароля, а не подобрать ваш сайт. Используйте одобренные именованные методы хеширования. Запрет всего IP-адреса в Интернете на одну секунду ограничит эффект параллельных атак, без каких-либо затруднений, которые может оценить человек. Наконец, если ваша система допускает более 1000 неудачных попыток входа в систему за одну секунду без какой-либо реакции на запрет системы, тогда ваши планы безопасности имеют более серьезные проблемы, над которыми нужно работать. Прежде всего исправьте этот автоматический ответ.

person Don Turnblade    schedule 03.12.2012

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.

или как было предложено Сайро:

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;
    }
}
person Tyler Carter    schedule 19.01.2010
comment
Вы также можете использовать: sleep (2 ^ (intval ($ _ SESSION ['hit']) - 1)); - person nortron; 19.01.2010
comment
Очевидная проблема заключается в том, что серьезный злоумышленник не будет беспокоиться об обработке файлов cookie, поэтому сеанс становится бесполезным. - person deceze♦; 19.01.2010
comment
сон (2 ^ (intval ($ count) - 1)); Мне вроде как нравится массив, поэтому я могу установить время ожидания, но мне любопытно, как это приравнивается? Кроме того, если бы мне пришлось сохранить в БД, после входа пользователя в систему я бы удалил там хиты из БД, чтобы это был новый старт, когда они попытаются войти в систему в следующий раз после входа в систему? - person JasonDavis; 19.01.2010
comment
Вы должны установить время истечения срока, так как задержка должна истечь через определенное время. Все остальное зависит от вас. Если кто-то входит / выходит из системы и пытается снова войти в систему, вы можете захотеть или не захотеть сохранить свой прошлый таймер задержки. Это ваш звонок. - person Tyler Carter; 19.01.2010
comment
Также помните, что ответ Cryo не использует массив. - person Tyler Carter; 19.01.2010
comment
-1, потому что любой, кто пытается это сделать, не включит куки для своего бота. - person Lotus Notes; 21.05.2010

Сохранять неудачные попытки в базе данных по 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!";
}
person Matchu    schedule 19.01.2010
comment
База данных - определенно способ сделать это. Таким образом, у вас также есть история, на которую можно оглянуться. - person sestocker; 19.01.2010
comment
Я думал о чем-то вроде этого, я думаю, что форумы vbulletin делают что-то вроде этого, сеанс можно сбросить, закрыв браузер и вернувшись, я думаю - person JasonDavis; 19.01.2010
comment
Вы можете объяснить, в какое время это создает pow (2, $ failed_attempts)? - person JasonDavis; 19.01.2010
comment
Я бы не советовал вам использовать режим сна, поскольку он блокирует этот экземпляр PHP до его завершения. Если злоумышленник откроет кучу соединений для перебора сервера, он очень быстро выполнит резервное копирование с помощью запросов PHP. Было бы лучше отклонить все попытки входа в систему в течение периода времени задержки для этого IP-адреса. - person Kendall Hopkins; 19.01.2010
comment
Смотрите документацию PHP для pow () - он возвращает 2 в степени $ failed_attempts :) - person Matchu; 19.01.2010
comment
@SoftwareElves Я думаю, что именно так работают форумы vbulletin: если вы не войдете в систему и попытаетесь снова слишком рано, они напечатают на экране сообщение о том, что попробуйте еще раз через X секунд - person JasonDavis; 19.01.2010
comment
Я бы шапку $remaining_delay = min(3600, $remaining_delay);. - person Alix Axel; 21.01.2010

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

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

person Sampson    schedule 19.01.2010
comment
В настоящее время я делаю что-то подобное, но я думал, что если бы была DoS-атака, я не был уверен, будет ли бот или что-то еще работать с сессиями, но я думаю, это должно было бы работать - person JasonDavis; 19.01.2010
comment
Бот может легко игнорировать файл cookie сеанса. Используйте базу данных с IP, и бот не может ничего с этим поделать, кроме переключения IP. - person Matchu; 19.01.2010
comment
@Matchu - если вы сделаете это, вы рискуете сделать тысячи или миллионы ненужных вызовов БД и обложить ваши ресурсы другими способами. Я считаю, что есть комбинированные решения, которые работают лучше, чем ваше предложение. - person JM4; 20.06.2012

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

person vicatcu    schedule 19.01.2010
comment
Верно, но иногда приходится сражаться палкой в ​​руке. - person Xeoncross; 21.01.2010

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

person sestocker    schedule 19.01.2010

Согласно обсуждению выше, сеансы, файлы cookie и IP-адреса неэффективны - злоумышленник может манипулировать всем.

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

e.g.

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

HTH

C.

person symcbean    schedule 19.01.2010
comment
Более практичный подход состоит в том, чтобы регистрировать все неудачные попытки входа в систему и проверять, вызывает ли беспокойство количество неудачных попыток за последние ~ 10 минут, иначе злоумышленник может просто продолжать чередовать имена пользователей. Я написал класс, который делает это за вас github.com/ejfrancis/BruteForceBlocker - person ejfrancis; 15.06.2014

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

person Community    schedule 21.01.2010
comment
Стандартные приложения, работающие на VPS или общих хостах, могут обрабатывать только около 5-30 запросов в секунду. Итак, ваш метод работает, но возможно, что вы сделаете 30 попыток, прежде чем вы сможете их заблокировать. Также проверьте журналы Apache на наличие подобных вещей (особенно для отправки запросов). - person Xeoncross; 21.01.2010

cballuo дал отличный ответ. Я просто хотел в ответ предоставить обновленную версию, поддерживающую mysqli. Я немного изменил столбцы таблицы / поля в sqls и другие мелочи, но это должно помочь всем, кто ищет эквивалент 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;
        }
     }        
  }
}
person thank_you    schedule 05.10.2012
comment
Я написал более свежую версию, преобразованную в класс и использующую PDO github.com/ejfrancis/BruteForceBlocker - person ejfrancis; 15.06.2014