Предотвращение перебора/отказов в обслуживании в PHP

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

  1. Пользователь отправляет данные для входа.
  2. Check if username and password is correct
    • If Yes, let them in.
    • If No, record a failed attempt in the database. Check if there's too many fails within a given timeframe (eg: 5 in 5 minutes):
      • If Yes, then pause execution for 10 seconds: sleep(10), then report a login failure to the user.
      • Немедленно сообщить пользователю об ошибке входа

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

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

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

sleep($numRequestsInLast5Minutes - 5)

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


person nickf    schedule 13.11.2009    source источник
comment
Вместо того, чтобы вводить сон, почему бы не ввести капчу после максимально допустимого количества неудачных попыток входа в систему?   -  person foxybagga    schedule 07.01.2013


Ответы (5)


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

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

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

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

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

If not password correct:
    sleep(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 CAPTCHA

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

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

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

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

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

person Kartoch    schedule 04.04.2013
comment
почему за это еще не проголосовали? Вероятно, лучший ответ здесь .. спасибо. Упомянув сон, я надеюсь, вы предлагаете просто заблокировать доступ к входу в систему? Потому что использование сна в php просто замедлит работу системы, если будет много запросов. - person Sam Vloeberghs; 11.04.2013
comment
Что касается сна, я имел в виду остановку выполнения скрипта перед ответом на запрос пользователя. Так что я не думаю, что сон может увеличить системный заряд... - person Kartoch; 12.04.2013
comment
Точнее, sleep() является локальным для обработки запроса пользователя, поэтому он не влияет на другие запросы... - person Kartoch; 12.04.2013
comment
но все запросы пользователей обрабатываются сервером ..?! - person Sam Vloeberghs; 12.04.2013
comment
Моя точка зрения была общей (я более уверен в Java/Servlet/JSP, чем в PHP), поэтому sleep() исключает прерывание пользовательского запроса и НЕ блокирует весь интерпретатор PHP. Я знаю, что это возможно в Java (каждый пользовательский запрос посвящен потоку), но PHP не является многопоточным, и страница справки по сну на php.net не очень ясно описывает реальное поведение, поэтому я не совсем уверен, как справиться сон, чтобы заблокировать только один запрос пользователя. - person Kartoch; 12.04.2013
comment
Судя по моим логам, злоумышленник может иметь разные IP-адреса и использовать их для непрерывной атаки методом полного перебора. Один был заблокирован, затем используйте другой и еще один ... Я думаю, что блокировка по IP на сегодня больше не работает. В некоторых случаях злоумышленник может использовать одновременные попытки входа в систему (попытаться войти одновременно) с 2 или более зомби. - person vee; 24.06.2019

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

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

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

person brianegge    schedule 13.11.2009
comment
sleep ... позволит одному пользователю создавать множество подключений. У вас есть какие-то источники для этого? Не то, чтобы я тебе не верил, я просто хотел бы побольше об этом почитать. - person nickf; 13.11.2009
comment
@nickf: ну, вам действительно не нужно читать исследовательскую работу, чтобы понять затруднительное положение использования sleep() в этой ситуации. Использование sleep() в основном приостанавливает выполнение сценария, то есть соединение остается открытым до тех пор, пока оно не продолжит работу. Существуют ограничения на количество одновременных подключений, которые может разрешить ваш сервер, поэтому с помощью sleep() вы технически упрощаете для хакеров DoS-атаки на ваш сервер... - person Lukman; 13.11.2009
comment
почему бы не перенаправить ipnr через .htaccess? - person Grumpy; 21.01.2012

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

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

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

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

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

I've developed this but not released it into the wild yet, so any feedback would be appreciated.

person alex    schedule 17.11.2009
comment
истекают ли неудачные входы в систему? то есть, если 2 месяца назад я забыл свой пароль и попробовал 6 разных способов, а теперь я ввел неправильный пароль, придется ли мне ждать 3 минуты, прежде чем повторить попытку? - person nickf; 17.11.2009
comment
Хм, кажется, я сделал что-то вроде if (timeNow - timeFailed) > 3600, а затем сбросил счетчик. - person alex; 19.11.2009
comment
Хороший DoS: злоумышленник может продолжать использовать ложные пароли для пользователя, чтобы запретить ему доступ. - person Kartoch; 04.04.2013
comment
@Kartoch Да, я считаю, что это лучше, чем в конечном итоге разрешить злоумышленнику доступ к своей учетной записи. - person alex; 04.04.2013

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

https://github.com/ejfrancis/BruteForceBlocker

он регистрирует все неудачные входы в систему по всему сайту в таблице БД, и если количество неудачных входов в систему за последние 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

переключатель ($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;

}

person ejfrancis    schedule 14.06.2014

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

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

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

person Andrew Keith    schedule 13.11.2009
comment
ИМХО, зависит от мотивации атакующего: если основная цель - легкий DOS одним пользователем, то да, возможная контрмера. Но если цель состоит в том, чтобы избежать грубой силы пароля, это зависит от поведения второго веб-сервера (пустышка? блокировка?). - person Kartoch; 12.04.2013