Предупреждение грубой силы /DoS в PHP

Я пытаюсь написать script для предотвращения попыток входа в грубую силу на веб-сайте, который я создаю. Логика выглядит примерно так:

  • Пользователь отправляет регистрационную информацию.
  • Проверьте правильность имени пользователя и пароля
    • Если да, включите их.
    • Если нет, запишите неудачную попытку в базе данных. Проверьте, не слишком ли много ошибок в течение заданного периода времени (например: 5 за 5 минут):
      • Если да, то приостановите выполнение в течение 10 секунд: sleep(10), затем сообщите пользователю об ошибке входа в систему.
      • Немедленно сообщите пользователю о неудаче входа в систему.

Объясняя это сотруднику, меня спросили, как это поможет, если хакер отправит, скажем, 1000 запросов за одну секунду. Будут ли первые 5 возвращаться сразу, а остальные оставшиеся 995 займут всего 10 секунд?

У меня есть подозрительное подозрение, что я не совсем понимаю, как работает HTTP - это ситуация выше, чем возможно, или существует ли ограничение на количество параллельных запросов, которые сервер будет обрабатывать с одного клиента?

Будет ли лучшее решение иметь увеличенное время сна?

sleep($numRequestsInLast5Minutes - 5)

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

Ответы

Ответ 1

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

Первое решение

If not password correct for a certain number of time:
    block the user
    send a reset link to the user

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

Второе решение

If not password correct:
    sleep(amount_of_time)

Вопрос: каково значение 'amount_of_time'?

Пользователь: может быть досадно ждать "amount_of_time" для каждой ошибки
Атакующий: продолжайте попытки, с более низким тестом/секундами

Третье решение

If not password correct:
    sleep(amount_of_time)
    amount_of_time = amount_of_time * 2

Пользователь: менее раздражает несколько ошибок паролей
Атакующий: заблокировать пользователя для подключения, отправив много неправильного пароля

Четвертое решение

If not password correct for a certain number of time:
    submit a catchpa

Пользователь: нужно разрешить CAPTCHA (не слишком сложно)
Атакующий: необходимо разрешить CAPTCHA (должно быть сложно)

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

Пятое решение

If not password correct for a certain number of time:
    block the ip
    (eventually) send a reset link

Пользователь. Пользователь может быть заблокирован, потому что он не может правильно запомнить его пароль.
Атакующий: пытается использовать один и тот же пароль с другим пользователем, потому что блокировка основана на количестве логинов пользователя.

Окончательное решение?

If several login attempts failed whatever is the user by an IP :
    print a CAPTCHA for this IP

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

Важные примечания

Является ли форма входа или ссылка для входа в систему, которая заблокирована? Блокировка формы входа бесполезна.

Устойчивость к грубой силе является ПЕРВОЙ проблемой сложности пароля, поэтому вам нужна строгая политика паролей (особенно в случае распределенной грубой силы).

Я не упоминаю факт, чтобы хэш ваши пароли с солью, вы уже делаете это правильно? Потому что, если проще получить доступ к базе паролей, чем грубая форсировка, злоумышленник выберет это решение ( "Цепочка только сильна, как слабая ссылка" ).

Ответ 2

Я бы предложил, если пользователь пробовал безуспешно, скажем более пяти раз и пять минут, вы сразу же возвращаете 503 Service Unavailable для этого IP-адреса. Когда вход завершается с ошибкой, вы можете использовать memcache, чтобы получить текущие неудачные попытки для IP-адреса, а затем увеличить сумму и сохранить ее обратно в memcache с истечением 5 минут.

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

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

Ответ 3

Я использовал что-то вроде этого...

  • Проверьте имя пользователя и пароль

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

    1.2 Каждый сбой делает ожидание между возможностью входа в систему с именем failCount * 30 секунд, вплоть до максимума (например, 10 минут).

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

Я разработал это, но не выпустил его в дикую природу, поэтому любая обратная связь будет оценена.

Ответ 4

Я не уверен, что такое лучшая практика, но при работе с DoS-атаками лучшей стратегией является фактическое перенаправление трафика в сторону с вашего сервера. Настройка тайм-аутов на самом деле не поможет, потому что вы все еще обрабатываете запрос и запускаете PHP.

Рассматривали ли вы настройку другого веб-сервера, на котором была запущена упрощенная версия вашей страницы входа? Когда пользователь пытается слишком много раз (например, тысячи раз), отправьте сообщение, чтобы настроить маршрутизатор и перенаправить этого пользователя на второй веб-сервер.

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

Ответ 5

Я создал класс, который заботится о защите от грубой силы в PHP.

https://github.com/ejfrancis/BruteForceBlocker

он регистрирует все сбойные логины на всей территории страны в таблице db, и если количество неудачных логинов за последние 10 минут (или любой другой временной интервал, который вы выберете) превышает установленный предел, он обеспечивает временную задержку и/или Требование перехватчика перед тем, как войти в систему снова.

Пример:

//создаем массив настроек дросселя. (# последние неудачные логины = > ответ).

$throttle_settings = [

    50 => 2,            //delay in seconds
    150 => 4,           //delay in seconds
    300 => 'captcha'    //captcha 

];

$BFBresponse = BruteForceBlocker:: getLoginStatus ($ throttle_settings);

//$throttle_settings - необязательный параметр. если он не включен, будет использоваться массив настроек по умолчанию в BruteForceBlocker.php

switch ($ BFBresponse ['status']) {

case 'safe':
    //safe to login
    break;
case 'error':
    //error occured. get message
    $error_message = $BFBresponse['message'];
    break;
case 'delay':
    //time delay required before next login
    $remaining_delay_in_seconds = $BFBresponse['message'];
    break;
case 'captcha':
    //captcha required
    break;

}