Как избежать DOS-атаки с использованием сокетов Berkeley в C++


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

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

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

Есть идеи?

5 4

5 ответов:

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

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

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

  1. имеют несколько потоков выполнения

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

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

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

  3. работают асинхронно

    Например., aio_read

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

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

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

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

Основное чтение: Задача C10K

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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