RabbitMQ: как сообщения могут утекать без остановки сервиса

Сервис работает. А данные уже прочитали

Утечка данных из RabbitMQ не обязательно сопровождается сбоем. Приложение обрабатывает задачи, очередь разгружается, пользователи не замечают отказа. Но часть сообщений могла пройти через постороннего получателя до того, как попала в приложение. Получатель (consumer) — это клиент, который получает сообщения из очереди.

Механизм прост: получить сообщение → прочитать содержимое → вернуть в очередь через NACK(requeue=true). Возврат сохраняет возможность дальнейшей обработки сообщения, но не отменяет уже состоявшееся чтение.

В этом и состоит риск: доступность сервиса и конфиденциальность сообщений — разные свойства. Успешно обработанная задача не доказывает, что её содержимое видел только нужный сервис.

Какие права для этого нужны

Речь не об обходе аутентификации и не о сетевой атаке «человек посередине» (MITM). Сценарий предполагает, что злоумышленник уже может подключиться к брокеру и имеет разрешение читать целевую очередь — например, из-за компрометации сервисной учётной записи или слишком широких прав в списках контроля доступа (ACL). Конфигурация также должна допускать ещё одного активного получателя. [1][3]

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

Получить, прочитать, вернуть

В обычной очереди сообщения распределяются между активными получателями, а не рассылаются каждому в виде копий. Часть сообщений достаётся приложению, часть может достаться постороннему получателю. Распределение зависит от лимита prefetch (числа допустимых неподтверждённых доставок), доступности получателей и настроек очереди. Эксклюзивный получатель (exclusive consumer) или режим Single Active Consumer могут не позволить другому получателю принимать сообщения одновременно с приложением. [1]

publisher → queue → дополнительный получатель
                         │
                         ├─ читает содержимое
                         └─ NACK(requeue=true) → queue → следующая доставка

В режиме ручных подтверждений доставленное сообщение остаётся неподтверждённым, пока получатель не ответит. ACK подтверждает получение и позволяет брокеру удалить сообщение из очереди. NACK отклоняет доставку; с параметром requeue=true сообщение снова становится доступным для доставки. Это не создание копии и не указание брокеру обязательно передать сообщение приложению. Следующим получателем может быть приложение — или тот же посторонний клиент. [2]

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

Почему это может остаться незамеченным

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

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

Нет гарантии, что сервис продолжит работать без изменений: возможны задержки, изменение порядка доставки, дополнительная нагрузка и циклы повторной доставки. Политики очереди, её тип и версия RabbitMQ также влияют на результат. [2]

Пример кода: возврат не отменяет чтение

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

Сохраните пример в delivery_model.py и запустите python delivery_model.py. Внешние библиотеки не нужны.

from collections import deque

# Synthetic data only: no network connection or external credentials.
queue = deque([{"id": "demo-1", "body": "synthetic payload"}])
observed = []
processed = []

# Model one possible delivery to an additional consumer.
message = queue.popleft()
observed.append(message.copy())
queue.appendleft(message)  # Model NACK(requeue=True), not a broker call.

# For this trace, explicitly choose the application as the next recipient.
# A real broker does not guarantee this recipient after requeue.
message = queue.popleft()
processed.append(message["id"])  # Model successful processing and ACK.

assert observed[0]["id"] == processed[0]
assert not queue
print("Observed:", observed[0]["id"])
print("Processed:", processed[0])
print("Queue empty:", not queue)

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

А если получателей больше?

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

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

Что проверять владельцу брокера

  • Учётные записи и ACL. Используйте отдельные учётные записи для сервисов, разрешайте чтение только нужных очередей и при необходимости изолируйте сервисы в отдельных виртуальных хостах. [3]
  • Получатели и подключения. Сравнивайте их с ожидаемой топологией, а не только проверяйте, разгружается ли очередь.
  • Повторные доставки и задержки. Их рост может иметь обычные причины, например сбои приложения, но требует объяснения.
  • Сетевой доступ. Ограничьте доступ к AMQP и Management API теми сегментами и сервисами, которым он действительно нужен.
  • Реакция на компрометацию. Отзовите доступ и завершите нежелательные соединения. Успешная обработка сообщений не исключает утечки.

Вывод

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

Источники

  1. RabbitMQ: Consumers
  2. RabbitMQ: Consumer Acknowledgements and Publisher Confirms
  3. RabbitMQ: Access Control