Как избежать атаки DOS с использованием сокетов Беркли в C ++ - PullRequest
5 голосов
/ 23 августа 2009

Я пробираюсь через Сетевое программирование UNIX Том 1 Ричарда Стивенса и пытаюсь написать TCP Echo Client, который использует протокол Telnet. Я все еще на ранних стадиях и пытаюсь написать функции чтения и записи.

Я хотел бы написать его для использования мультиплексирования ввода / вывода и функции Select, потому что он должен быть мультиклиентным, и я не хочу пытаться заниматься изучением потоков C ++, пока я пытаюсь изучить Библиотека сокетов Беркли одновременно. В конце главы о мультиплексировании ввода / вывода Стивенс имеет небольшой раздел о атаках DOS, где он говорит, что метод, который я планировал использовать, уязвим для атак DOS, которые просто отправляют один байт после подключения и затем зависают. Затем он упоминает 3 возможных решения - неблокирующий ввод-вывод, многопоточность (выход) и установка таймаута для операций ввода-вывода.

У меня вопрос: есть ли другие способы избежать такой атаки? А если нет, то какой из них лучше? Я просмотрел раздел о том, как установить таймаут для операций, но это не похоже на то, что я хочу сделать. Методы, которые он предлагает для этого, выглядят довольно сложными, и я не уверен, как превратить их в то, что у меня уже есть. Я только взглянул на главу о NIO, похоже, сейчас это путь, но я хотел бы узнать, есть ли другие способы обойти это, прежде чем я потрачу еще пару часов на просмотр этой главы.

Есть идеи?

Ответы [ 5 ]

4 голосов
/ 23 августа 2009

Основное значение: Проблема C10K

Использование потоков (или процессов) для каждого соединения делает код очень простым. Ограничение на количество соединений на самом деле является ограничением на количество потоков, которые ваша система может комфортно выполнять в нескольких задачах.

Использование асинхронного ввода-вывода для помещения всех сокетов в один поток - не такой простой код (красиво упакованный в libevent и libev2 ), но гораздо более масштабируемый - он ограничен числом открытых файлов, обрабатывающих вашу систему позволяет и - на последних сборках Linux, например - это может быть измерено в миллионах! По этой причине большинство веб-серверов и других серверов используют асинхронный ввод-вывод.

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

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

3 голосов
/ 23 августа 2009

... есть ли другие способы избежать такой атаки?

Да, асинхронный ввод / вывод - это еще один общий подход.

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

  1. Наличие нескольких потоков выполнения

    многопоточный, многопроцессный, оба.

  2. Ограничение по времени операции блокировки

    например, мгновенный (неблокирующий ввод / вывод) или нет (SO_RCVTIMEO, alarm() и т. Д.)

  3. Работать асинхронно

    , например, aio_read

... какой из них лучший?

Для новичка я бы предложил неблокирующий ввод / вывод в сочетании с ограниченным по времени select() / poll(). Ваше приложение может отслеживать, генерировало ли соединение «достаточно данных» (например, целую строку) за «достаточно короткое время».

Это мощная, в основном портативная и распространенная техника.

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

2 голосов
/ 23 августа 2009

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

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

1 голос
/ 23 августа 2009

То, что я сделал раньше, чтобы помочь с этим (около 1997 года :)), потребовало, чтобы магическое число было отправлено в течение определенного периода времени, иначе соединение было закрыто.

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

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

Таким образом, для очистки требуется основной поток и второй поток, поэтому он не однопоточный.

1 голос
/ 23 августа 2009

У меня вопрос, есть ли другие способы избежать такой атаки?

Для сервера я бы хотел таймер на уровне приложения:

  • Буфер входных данных на соединение
  • Код чтения из тупого сокета считывает данные из сокета во входной буфер
  • Код приложения анализирует содержимое входного буфера

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

Это подразумевает асинхронный ввод-вывод или выделенный поток ввода-вывода [s].

...