RabbitMQ: how messages can leak without a service outage
Table of Contents
An unauthorized consumer can read a message and requeue it with NACK. Why a working service does not guarantee message confidentiality. A RabbitMQ data leak does not necessarily look like an outage. The application processes jobs, the queue drains, and users see no failure. Yet some messages may have passed through an unauthorized consumer before reaching the application. A consumer is a client that receives messages from a queue. The mechanism is receive a message → read its contents → return it to the queue with That is the security issue: service availability and message confidentiality are different properties. A successfully processed job does not prove that only the intended service saw its contents. This is not an authentication bypass or a network man-in-the-middle (MITM) attack. The scenario assumes an attacker can already connect to the broker and has permission to read the target queue, perhaps through a compromised service account or overly broad access control lists (ACLs). The configuration must also allow another active consumer. [1][3] Several active consumers on an ordinary queue share deliveries rather than each receiving a copy of every message. Some messages go to the application; others may go to the unauthorized consumer. Distribution depends on the prefetch limit (the number of unacknowledged deliveries allowed), consumer availability, and queue configuration. An exclusive consumer or Single Active Consumer mode can prevent another consumer from receiving messages alongside the application. [1] With manual acknowledgements, a delivered message remains unacknowledged until the consumer responds. If the application subsequently receives and successfully processes the message, the business operation may complete without an apparent failure. Confidentiality has already been lost. Monitoring that checks only application availability and job completion may produce no obvious alert about the extra reader. The mechanism does not require changing application code or deliberately stopping the service. But unnoticed does not mean invisible. Additional consumers and connections appear at the broker, and redeliveries occur. These are observable changes. An absence of user complaints is no substitute for monitoring them. There is no guarantee that the service will remain unaffected: delays, changes in delivery order, additional load, and redelivery loops are possible. Queue policies, queue type, and RabbitMQ version also affect the outcome. [2] This is a local model of one possible sequence, not a RabbitMQ client. It uses a synthetic message in memory, makes no connections, and demonstrates the key point: successful processing can follow disclosure to another participant. Save it as The example deliberately chooses the application as the next recipient. Real RabbitMQ requeueing makes no such guarantee. This model does not measure delivery distribution, processing speed, or how noticeable a connection is. Consumer count can affect delivery distribution, but there is no universal formula for the share of messages an unauthorized consumer can read. The proportion of unauthorized consumers does not equal the proportion of unique messages they observe: receiving the same message again does not increase that share, and a message already acknowledged by the application is no longer available from the queue. More connections also mean more observable participants and potentially more load. This neither guarantees complete interception nor makes the activity invisible. The danger of “read and return” is not guaranteed invisibility. It is that disclosure may happen without an obvious service failure. A message can return to the queue and be processed, but the data already read cannot be taken back.The service works. Someone else has read the data
NACK(requeue=true). Returning it preserves the possibility of subsequent processing, but does not undo the read.What access is required
read permission does not mean passive viewing. A consumer participates in delivery. TLS protects the connection, not the contents from a client the broker permits to receive them.Receive, read, return
publisher → queue → additional consumer
│
├─ reads the contents
└─ NACK(requeue=true) → queue → next deliveryACK confirms receipt and allows the broker to remove the message from the queue. NACK rejects the delivery; with requeue=true, it makes the message available for redelivery. This does not create a copy or instruct the broker to deliver specifically to the application. The next recipient may be the application—or the same consumer again. [2]Why it might go unnoticed
Code example: returning does not undo reading
delivery_model.py and run python delivery_model.py. No external packages are required.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)What if there are more consumers?
What broker owners should check
Conclusion
Sources