Предупреждение грубой силы /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;
}