MQTT-Rate-Limiting ist die Art, wie ein Broker sich selbst und seine Nutzer vor Clients schützt, die zu viel, zu schnell oder zu Großes senden. Dieser Artikel erklärt die wichtigsten Denial-of-Service-Muster bei MQTT, die Limits, die Broker dagegen einsetzen, wie ConnectMQ Quotas und Flood Protection anwendet und wie Sie Clients schreiben, die sich zurücknehmen, statt das Problem zu verschärfen. Die meisten Rate-Limit-Vorfälle werden eher durch fehlerhafte Firmware als durch Angreifer verursacht, daher zählen beide Seiten.
Warum MQTT Rate Limiting braucht
Ein MQTT-Broker ist eine gemeinsam genutzte Ressource. Jede Verbindung kostet Speicher, jede Nachricht kostet CPU und Bandbreite, und jede Subscription erhöht den Routing-Aufwand. Ohne Limits kann ein einziger fehlerhafter Client den Dienst für alle anderen Clients auf demselben Broker beeinträchtigen.
Die Bedrohung ist nicht nur böswillig. Häufige Ursachen für Überlast sind:
- Ein Firmware-Bug, der in einer engen Schleife publiziert statt einmal pro Minute.
- Ein Edge-Gateway, das jeden SPS-Zyklus (etwa alle 10 ms) als eigene Nachricht publiziert, statt Werte zu aggregieren oder nur Änderungen zu senden.
- Eine Reconnect-Schleife ohne Verzögerung nach einer Netzwerkstörung, wiederholt in einer ganzen Flotte.
- Eine Kamera oder ein Logger, der versehentlich große Binärblöcke publiziert.
- Ein Dashboard, das auf einem stark ausgelasteten Broker
#abonniert und jede Nachricht verteilt. - Das Erraten von Zugangsdaten, das eine Verbindung nach der anderen öffnet.
Rate Limiting macht aus diesen Fehlern statt Ausfällen eingegrenzte, sichtbare Fehlermeldungen.
Die wichtigsten DoS-Muster bei MQTT
| Muster | Was passiert | Typische Abwehr |
|---|---|---|
| Verbindungsflut | Viele TCP-/TLS-Verbindungen werden schnell geöffnet, jede kostet einen Handshake und Speicher | Verbindungsratenlimits pro IP, maximale Verbindungen pro Mandant, IP-Sperre |
| Reconnect-Sturm | Tausende Geräte verbinden sich nach einem Ausfall im selben Moment neu | Clientseitiges exponentielles Backoff mit Jitter |
| Publish-Sturm | Ein Client sendet weit mehr Nachrichten pro Sekunde als erwartet | Nachrichtenratenlimits pro Client, Trennung bei Missbrauch |
| Übergroße Payloads | Riesige Nachrichten verbrauchen Speicher und Bandbreite | Maximale Paketgröße |
| Subscription-Explosion | Ein Client legt viele Subscriptions oder breite Wildcards an | Maximale Subscriptions pro Client, Limits für die Topic-Tiefe |
| Aufgeblähte Retained Messages oder Sessions | Viele Retained-Topics oder langlebige Sessions mit gepufferten Nachrichten | Limits für Retained Messages, Session Expiry |
| Langsame Konsumenten | Subscriber, die nicht mithalten, lassen Warteschlangen wachsen | Queue-Limits, QoS-Wahl, Message Expiry |
| Erraten von Zugangsdaten | Wiederholte fehlgeschlagene Logins | Drosselung fehlgeschlagener Logins und IP-Sperre |
Wie Broker Limits durchsetzen
Limits auf Verbindungsebene
Am günstigsten lässt sich Missbrauch stoppen, bevor ein Client authentifiziert ist. Broker begrenzen, wie viele Verbindungen eine einzelne IP pro Sekunde öffnen darf, deckeln die Gesamtzahl der Verbindungen pro Konto oder Plan und sperren Quellen, die wiederholt an der Authentifizierung scheitern. Ein TLS-Handshake ist relativ teuer, daher schützt die Drosselung in dieser Phase CPU und Speicher gleichermaßen.
Limits auf Nachrichtenebene
Nach dem Verbindungsaufbau kann der Broker Nachrichten pro Sekunde und Bytes pro Sekunde pro Client verfolgen. Überschreitet ein Client sein Budget, hat der Broker einige Optionen: die Verarbeitung verzögern, Nachrichten verwerfen, sie mit einem Reason Code ablehnen oder den Client trennen. MQTT 5 macht das wesentlich transparenter als MQTT 3.1.1.
Limits für die Paketgröße
Jeder Broker hat eine maximale Paketgröße. MQTT 5 erlaubt beiden Seiten, sie anzukündigen: Der Broker sendet Maximum Packet Size im CONNACK, und Clients sollten nie mehr senden. Eine Nachricht über dem Limit ist ein Protokollfehler und führt in der Regel zur Trennung.
Limits für Subscriptions und Topics
Obergrenzen für Subscriptions pro Client und für die Topic-Tiefe verhindern, dass Clients unbegrenzten Routing-Aufwand erzeugen.
Hilfreiche Funktionen von MQTT 5
MQTT 5 hat mehrere Mechanismen eingeführt, die Limits explizit statt rätselhaft machen. Die Übersicht der Funktionen von MQTT 5 behandelt sie ausführlicher.
- Reason Codes. Ein Broker kann auf ein PUBACK oder DISCONNECT mit
0x97 Quota exceededoder0x96 Message rate too highantworten, statt Daten stillschweigend zu verwerfen. Clients können den Grund protokollieren und langsamer werden. - Receive Maximum. Beide Seiten kündigen an, wie viele unbestätigte QoS-1- und QoS-2-Nachrichten sie gleichzeitig annehmen, was eine einfache Flusssteuerung ermöglicht.
- Maximum Packet Size. Wird in CONNECT und CONNACK angekündigt, sodass übergroße Nachrichten vermieden statt abgelehnt werden.
- Message Expiry. Alte Nachrichten werden verworfen, statt sich für Offline-Subscriber anzustauen.
- Session Expiry. Verwaiste Sessions werden automatisch aufgeräumt.
- Server Keep Alive. Der Broker kann das Keep-Alive-Intervall eines Clients überschreiben.
Mit MQTT 3.1.1 kann ein Broker, der Limits durchsetzt, meist nur Nachrichten verwerfen oder die Verbindung schließen, und der Client sieht einen generischen Netzwerkfehler.
Limits bei ConnectMQ
ConnectMQ wendet Limits auf mehreren Ebenen an, um die Mandanten auf seinen gemeinsam genutzten Clustern zu schützen:
- Quotas pro Plan. Der kostenlose Serverless-Plan erlaubt bis zu 25 Verbindungen, QoS bis 1 und Nachrichten bis 256 KB. Der kostenpflichtige Serverless-Plan erhöht das auf bis zu 1.000 Verbindungen, QoS 2 und Nachrichten bis 1 MB. Dedicated- und BYOC-Deployments laufen auf isolierter Infrastruktur.
- Signalisierung von Quotas. Wird eine Quota überschritten, erhalten MQTT-5-Clients den Reason Code 0x97 (quota exceeded), sodass Sie ein Limit von einem Netzwerkfehler unterscheiden können.
- Flood Protection gegen missbräuchliche Verbindungs- und Nachrichtenmuster.
- IP-Sperre nach wiederholten fehlgeschlagenen Logins.
- Mandantenisolation, sodass der Traffic eines Mandanten die Topics eines anderen nicht erreichen kann.
Der öffentliche MQTT-Broker broker.connectmq.com hat strengere Limits pro Client, weil er für alle offen ist: 64 KB pro Nachricht, 10 Nachrichten pro Sekunde pro Client, 10 Subscriptions pro Client, 8 Topic-Ebenen und Session Expiry bis 30 Minuten. Wenn ein Test an diese Grenzen stößt, ist das ein deutlicher Hinweis, die Last auf einen privaten Broker zu verlagern. Der Artikel zum kostenlosen öffentlichen MQTT-Broker listet die Details auf.
Clients schreiben, die Limits respektieren
Serverseitige Limits sind nur die halbe Geschichte. Eine Flotte gut erzogener Clients ist der beste DoS-Schutz, den Sie haben können.
- Beim Reconnect zurückhalten. Nutzen Sie exponentielles Backoff mit zufälligem Jitter, damit sich Geräte nach einem Ausfall nicht im Gleichschritt neu verbinden.
- Bündeln und aggregieren. Senden Sie eine Nachricht mit zehn Messwerten statt zehn Nachrichten, wo die Latenz es erlaubt.
- Bei Änderung publizieren, nicht bei jedem Schleifendurchlauf. Fügen Sie Totbänder hinzu, damit kleinste Schwankungen keinen Traffic erzeugen. Dieses Prinzip (Report by Exception) ist auch in Sparkplug B und vielen Unified-Namespace-Architekturen in der Fertigung üblich.
- Die angekündigte Paketgröße respektieren. Lesen Sie
Maximum Packet Sizeaus den CONNACK-Properties. - Reason Codes behandeln. Protokollieren Sie 0x97 und 0x96 und senken Sie Ihre Publish-Rate, statt sofort erneut zu senden.
- Subscriptions eng halten. Abonnieren Sie die Topics, die Sie brauchen, nicht
#. - Eindeutige Client IDs verwenden. Zwei Geräte mit derselben ID werfen sich gegenseitig in einer Endlosschleife hinaus, die wie eine Flut aussieht.
Hier ein Python-Client mit paho-mqtt 2.x, der Backoff anwendet, Quota-Reason-Codes behandelt und sein eigenes Publizieren drosselt:
import json
import random
import time
import paho.mqtt.client as mqtt
HOST, PORT = "broker.connectmq.com", 8883
CLIENT_ID = f"rate-demo-{random.randint(1000, 9999)}"
TOPIC = f"connectmq/demo/{CLIENT_ID}/telemetry"
MAX_RATE = 2.0 # Nachrichten pro Sekunde, deutlich unter dem Limit des öffentlichen Brokers
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID,
protocol=mqtt.MQTTv5)
client.tls_set()
# Für einen privaten Broker ergänzen: client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.reconnect_delay_set(min_delay=1, max_delay=120) # exponentielles Backoff
slow_down_until = 0.0
def on_connect(c, userdata, flags, reason_code, properties):
print("connected:", reason_code)
if properties and hasattr(properties, "MaximumPacketSize"):
print("broker max packet size:", properties.MaximumPacketSize)
def on_publish(c, userdata, mid, reason_code, properties):
global slow_down_until
if reason_code.is_failure:
print(f"publish {mid} refused: {reason_code}")
if reason_code.value in (0x96, 0x97):
slow_down_until = time.time() + 30 # 30 s pausieren
def on_disconnect(c, userdata, flags, reason_code, properties):
print("disconnected:", reason_code)
client.on_connect = on_connect
client.on_publish = on_publish
client.on_disconnect = on_disconnect
client.connect(HOST, PORT, keepalive=60)
client.loop_start()
interval = 1.0 / MAX_RATE
try:
while True:
if time.time() < slow_down_until:
time.sleep(1)
continue
payload = json.dumps({"t": round(20 + random.random(), 2), "ts": int(time.time())})
client.publish(TOPIC, payload, qos=1)
time.sleep(interval)
except KeyboardInterrupt:
client.loop_stop()
client.disconnect()
Dieselben Prinzipien gelten für Firmware. Bauen Sie auf einem ESP32 vor jedem Reconnect-Versuch eine Verzögerung mit Jitter ein und rufen Sie publish() nie ohne Timer aus einer Schleife auf; das ESP32-MQTT-Tutorial zeigt ein nicht blockierendes Muster.
Rate-Limit-Probleme überwachen
Achten Sie in Ihren Client-Logs und Broker-Dashboards auf diese Signale:
- Spitzen bei Verbindungsabbrüchen über viele Geräte hinweg im selben Moment.
- Wiederholte Reason Codes 0x97 oder 0x96 vom selben Client.
- Geräte, die sich in einem Zyklus von wenigen Sekunden verbinden, publizieren und trennen.
- Eine Nachrichtenrate, die nicht zum konfigurierten Meldeintervall passt.
Bei ConnectMQ helfen Ihnen das Dashboard im Portal und der Live-Topic-Explorer, einen geschwätzigen Client zu erkennen, und mit $SYS/broker/connection/{clientId}/state können Sie den Verbindungsstatus eines bestimmten Clients beobachten.
Wann ein größerer Plan sinnvoll ist
Wenn legitimer Traffic und kein Bug an Limits stößt, liegt die Lösung in mehr Kapazität statt in strengeren Clients. Anzeichen sind ein stetiges Wachstum verbundener Geräte, Bedarf an QoS 2 oder Payloads über 256 KB. Der Leitfaden zum verwalteten MQTT-Broker erklärt, wie sich Serverless- und Dedicated-Optionen unterscheiden, und der verwandte Artikel über offen erreichbare MQTT-Broker zeigt, warum selbst gehostete Broker ohne Limits ein Risiko sind.
Starten Sie mit ConnectMQ
Erstellen Sie unter app.connectmq.com einen Broker im kostenlosen Serverless-Plan, ganz ohne Kreditkarte, verbinden Sie den obigen Client mit Ihren generierten Zugangsdaten und sehen Sie, wie sich Quotas und Reason Codes in der Praxis verhalten. Weiterführende Hinweise finden Sie unter Best Practices für MQTT-Sicherheit.
Häufig gestellte Fragen
Was bedeutet der MQTT-Reason-Code 0x97?
Er bedeutet „Quota exceeded". Der Broker teilt dem Client mit, dass er ein erlaubtes Limit überschritten hat, daher sollte der Client langsamer werden, statt sofort erneut zu senden.
Unterstützt MQTT 3.1.1 Rückmeldungen zu Rate Limits?
Nicht direkt. MQTT 3.1.1 kennt keine Reason Codes im PUBACK, ein Broker kann also nur Nachrichten verwerfen oder trennen. Stellen Sie Clients auf MQTT 5 um, um explizite Rückmeldungen zu erhalten.
Sollte ich auch auf dem Gerät drosseln?
Ja. Clientseitige Drosselung, Bündelung und Reconnect-Backoff verhindern die meisten Vorfälle und halten Sie deutlich innerhalb der Broker-Limits.
Was ist ein Reconnect-Sturm?
Er entsteht, wenn viele Geräte gleichzeitig die Verbindung verlieren und alle sofort neu verbinden wollen. Exponentielles Backoff mit zufälligem Jitter verteilt die Neuverbindungen über die Zeit.



