Как избежать атаки DOS с помощью сокетов Berkeley в C++

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

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

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

Любые идеи?


person Daniel Bingham    schedule 22.08.2009    source источник


Ответы (5)


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

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

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

  1. Иметь несколько потоков выполнения#P4#
  2. Ограничение по времени операции блокировки#P5#
  3. Operate asynchronously

    e.g., aio_read

... which of these is the best?

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

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

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

person pilcrow    schedule 23.08.2009

Важное чтение: Проблема C10K

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

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

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

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

person Will    schedule 22.08.2009

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

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

person Jim Lewis    schedule 22.08.2009
comment
Я только начинаю знакомиться с ядром Berkley Sockets. Я написал пару сетевых приложений на Java с использованием сокетов Java. И я запускал и кодировал MUD с использованием сокетов в течение многих лет - хотя я использовал базу кода для этого и только модифицировал - не писал часть сокета. У меня довольно хорошее понимание того, как должно работать сетевое программирование, и теории, стоящей за ним, это просто изучение особенностей сокетов C/C++. Кроме того, не спрашивая обо всех DOS-атаках или обо всех атаках. Как раз тот, который вызывает блокировку. - person Daniel Bingham; 23.08.2009

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

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

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

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

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

person ChrisW    schedule 22.08.2009

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

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

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

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

person James Black    schedule 23.08.2009