Seguridad MQTT

Limitación de tasa y protección DoS en MQTT explicadas

Cómo se defienden los brokers MQTT de las inundaciones de conexiones, las tormentas de publicaciones y los payloads sobredimensionados, qué límites aplica un broker MQTT gestionado y cómo escribir clientes que los respeten.

Diagrama de limitación de tasa en MQTT con un broker que frena una tormenta de publicaciones mientras los dispositivos IoT normales siguen enviando mensajes
Pruébalo en tu propio broker MQTTConsigue un broker MQTT privado y gestionado con el plan Free y empieza con ejemplos de código listos para usar.
Pruébalo gratis Ejemplos de código Sin tarjeta

La limitación de tasa en MQTT es la forma en que un broker se protege a sí mismo y a sus usuarios de los clientes que envían demasiado, demasiado rápido o demasiado grande. Este artículo explica los principales patrones de denegación de servicio en MQTT, los límites que usan los brokers contra ellos, cómo aplica ConnectMQ cuotas y protección contra inundaciones y cómo escribir clientes que reduzcan el ritmo en lugar de agravar el problema. La mayoría de los incidentes por límites de tasa se deben a firmware con errores y no a atacantes, así que ambos lados importan.

Por qué MQTT necesita limitación de tasa

Un broker MQTT es un recurso compartido. Cada conexión consume memoria, cada mensaje consume CPU y ancho de banda, y cada suscripción añade trabajo de enrutamiento. Sin límites, un solo cliente con mal comportamiento puede degradar el servicio para todos los demás clientes del mismo broker.

La amenaza no es solo maliciosa. Entre las causas habituales de sobrecarga están:

  • Un fallo de firmware que publica en un bucle cerrado en lugar de una vez por minuto.
  • Un bucle de reconexión sin espera tras un fallo de red, repetido en toda una flota.
  • Una cámara o un registrador que publica por error grandes blobs binarios.
  • Un dashboard que se suscribe a # en un broker con mucho tráfico y recibe todos los mensajes.
  • Intentos de adivinar credenciales que abren una conexión tras otra.

La limitación de tasa convierte estos fallos en errores contenidos y visibles en lugar de caídas del servicio.

Los principales patrones DoS en MQTT

Patrón Qué ocurre Defensa típica
Inundación de conexiones Se abren rápidamente muchas conexiones TCP/TLS, cada una con su handshake y su memoria Límites de tasa de conexión por IP, máximo de conexiones por tenant, bloqueo de IP
Tormenta de reconexiones Miles de dispositivos se reconectan en el mismo instante tras una caída Backoff exponencial con jitter en el cliente
Tormenta de publicaciones Un cliente envía muchos más mensajes por segundo de lo previsto Límites de tasa de mensajes por cliente, desconexión ante abusos
Payloads sobredimensionados Mensajes enormes consumen memoria y ancho de banda Tamaño máximo de paquete
Explosión de suscripciones Un cliente crea muchas suscripciones o comodines amplios Máximo de suscripciones por cliente, límites de profundidad de topics
Acumulación de retenidos o sesiones Muchos topics retenidos o sesiones de larga duración con mensajes en cola Límites de mensajes retenidos, caducidad de sesión
Consumidores lentos Suscriptores que no dan abasto hacen crecer las colas Límites de cola, elección de QoS, caducidad de mensajes
Adivinación de credenciales Inicios de sesión fallidos repetidos Limitación de inicios de sesión fallidos y bloqueo de IP

Cómo aplican los límites los brokers

Límites a nivel de conexión

El lugar más barato para frenar los abusos es antes de que el cliente se autentique. Los brokers limitan cuántas conexiones puede abrir por segundo una misma IP, ponen un tope al total de conexiones por cuenta o plan y bloquean los orígenes que fallan la autenticación repetidamente. Un handshake TLS es relativamente costoso, así que limitar en esta fase protege tanto la CPU como la memoria.

Límites a nivel de mensaje

Una vez conectado el cliente, el broker puede contar los mensajes por segundo y los bytes por segundo de cada cliente. Cuando un cliente supera su presupuesto, el broker tiene varias opciones: retrasar el procesamiento, descartar mensajes, rechazarlos con un código de motivo o desconectar al cliente. MQTT 5 hace esto mucho más transparente que MQTT 3.1.1.

Límites de tamaño de paquete

Todo broker tiene un tamaño máximo de paquete. MQTT 5 permite que ambas partes lo anuncien: el broker envía Maximum Packet Size en el CONNACK y los clientes nunca deben superarlo. Un mensaje por encima del límite es un error de protocolo y normalmente provoca la desconexión.

Límites de suscripciones y topics

Los topes de suscripciones por cliente y de profundidad de topics impiden que los clientes generen un trabajo de enrutamiento ilimitado.

Funcionalidades de MQTT 5 que ayudan

MQTT 5 añadió varios mecanismos que hacen explícitos los límites en lugar de misteriosos. La descripción general de las funcionalidades de MQTT 5 los trata con más detalle.

  • Códigos de motivo. Un broker puede responder con 0x97 Quota exceeded en un PUBACK o un DISCONNECT, o con 0x96 Message rate too high, en lugar de descartar datos en silencio. Los clientes pueden registrar el motivo y reducir el ritmo.
  • Receive Maximum. Ambas partes anuncian cuántos mensajes QoS 1 y QoS 2 sin confirmar aceptarán en vuelo, lo que proporciona un control de flujo básico.
  • Maximum Packet Size. Se anuncia en CONNECT y CONNACK para evitar los mensajes sobredimensionados en lugar de rechazarlos.
  • Caducidad de mensajes. Los mensajes antiguos se descartan en lugar de acumularse para los suscriptores desconectados.
  • Caducidad de sesión. Las sesiones abandonadas se limpian automáticamente.
  • Server Keep Alive. El broker puede sustituir el intervalo de keep-alive del cliente.

Con MQTT 3.1.1, un broker que aplica límites normalmente solo puede descartar mensajes o cerrar la conexión, y el cliente ve un error de red genérico.

Límites en ConnectMQ

ConnectMQ aplica límites en varias capas para proteger a los tenants de sus clústeres compartidos:

  • Cuotas por plan. El plan Serverless gratuito permite hasta 25 conexiones, QoS hasta 1 y mensajes de 256 KB. El plan Serverless de pago lo amplía hasta 1.000 conexiones, QoS 2 y mensajes de 1 MB. Los despliegues Dedicated y BYOC funcionan sobre infraestructura aislada.
  • Señalización de cuotas. Cuando se supera una cuota, los clientes MQTT 5 reciben el código de motivo 0x97 (quota exceeded), para que puedas distinguir un límite de un fallo de red.
  • Protección contra inundaciones frente a patrones abusivos de conexiones y mensajes.
  • Bloqueo de IP tras inicios de sesión fallidos repetidos.
  • Aislamiento de tenants, para que el tráfico de un tenant no pueda llegar a los topics de otro.

El servidor MQTT público gratuito, broker.connectmq.com, tiene límites por cliente más estrictos porque está abierto a todo el mundo: 64 KB por mensaje, 10 mensajes por segundo por cliente, 10 suscripciones por cliente, 8 niveles de topic y caducidad de sesión de hasta 30 minutos. Si una prueba alcanza estos límites, es una señal clara de que conviene trasladar la carga a un broker privado. El artículo dedicado al servidor público recoge los detalles y recuerda que es solo para pruebas, no para datos de producción.

Escribir clientes que respeten los límites

Los límites del lado del servidor son solo la mitad de la historia. Una flota de clientes bien educados es la mejor protección DoS que puedes tener.

  1. Aplica backoff al reconectar. Usa backoff exponencial con jitter aleatorio para que los dispositivos no se reconecten al unísono tras una caída.
  2. Agrupa y agrega. Envía un mensaje con diez lecturas en lugar de diez mensajes, cuando la latencia lo permita.
  3. Publica cuando haya cambios, no en cada iteración. Añade bandas muertas para que las pequeñas fluctuaciones no generen tráfico.
  4. Respeta el tamaño de paquete anunciado. Lee Maximum Packet Size de las propiedades del CONNACK.
  5. Gestiona los códigos de motivo. Registra 0x97 y 0x96 y reduce la tasa de publicación en lugar de reintentar de inmediato.
  6. Mantén las suscripciones acotadas. Suscríbete a los topics que necesitas, no a #.
  7. Usa client IDs únicos. Dos dispositivos que comparten ID se expulsarán mutuamente en un bucle infinito que parece una inundación.

Este es un cliente en Python con paho-mqtt 2.x que aplica backoff, gestiona los códigos de motivo de cuota y limita su propia publicación:

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  # mensajes por segundo, muy por debajo del límite del broker público

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID,
                     protocol=mqtt.MQTTv5)
client.tls_set()
# Para un broker privado añade: client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.reconnect_delay_set(min_delay=1, max_delay=120)  # backoff exponencial

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  # pausa de 30 s

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()

Los mismos principios se aplican al firmware. En un ESP32, introduce una espera con jitter antes de cada intento de reconexión y nunca llames a publish() desde un bucle sin temporizador; el tutorial de MQTT con ESP32 muestra un patrón no bloqueante.

Supervisar los problemas de límites de tasa

Vigila estas señales en los logs de tus clientes y en los dashboards del broker:

  • Picos de desconexiones en muchos dispositivos en el mismo instante.
  • Códigos de motivo 0x97 o 0x96 repetidos del mismo cliente.
  • Dispositivos que se conectan, publican y se desconectan en ciclos de pocos segundos.
  • Una tasa de mensajes que no coincide con el intervalo de envío configurado.

En ConnectMQ, el dashboard del portal y el explorador de topics en vivo te ayudan a detectar un cliente demasiado parlanchín, y $SYS/broker/connection/{clientId}/state te permite seguir el estado de conexión de un cliente concreto.

Cuándo pasar a un plan mayor

Si es el tráfico legítimo, y no un fallo, el que alcanza los límites, la solución es más capacidad y no clientes más restringidos. Algunas señales son el crecimiento constante de dispositivos conectados, la necesidad de QoS 2 o payloads de más de 256 KB. La guía del broker MQTT gestionado explica en qué se diferencian las opciones serverless y dedicadas, y el artículo relacionado sobre brokers MQTT expuestos muestra por qué los brokers autoalojados sin límites son un riesgo.

Empieza con ConnectMQ

Crea un broker en el plan Serverless gratuito en app.connectmq.com sin tarjeta de crédito, conecta el cliente anterior con tus credenciales generadas y observa en la práctica cómo se comportan las cuotas y los códigos de motivo. Para una orientación más amplia, lee las buenas prácticas de seguridad MQTT.

Preguntas frecuentes

¿Qué significa el código de motivo 0x97 de MQTT?

Significa "Quota exceeded". El broker indica al cliente que ha superado un límite permitido, así que el cliente debe reducir el ritmo en lugar de reintentar de inmediato.

¿Admite MQTT 3.1.1 avisos de límite de tasa?

No directamente. MQTT 3.1.1 no tiene códigos de motivo en PUBACK, así que un broker solo puede descartar mensajes o desconectar. Actualiza los clientes a MQTT 5 para recibir avisos explícitos.

¿Debo limitar la tasa también en el dispositivo?

Sí. La limitación en el cliente, la agrupación de mensajes y el backoff al reconectar evitan la mayoría de los incidentes y te mantienen holgadamente dentro de los límites del broker.

¿Qué es una tormenta de reconexiones?

Ocurre cuando muchos dispositivos pierden la conexión a la vez y todos reintentan de inmediato. El backoff exponencial con jitter aleatorio reparte las reconexiones en el tiempo.

Artículos relacionados

Cómo rotar credenciales MQTT sin tiempo de inactividad

Una estrategia de doble credencial paso a paso para rotar usuarios y contraseñas MQTT en toda una flota: prepara la nueva credencial, cambia con respaldo, verifica y solo entonces revoca la antigua.

8 min de lectura

Todos los artículos →

Tu broker MQTT privado, a unos pocos clics

Crea una cuenta, obtén las credenciales de tu broker y conecta tu primer dispositivo, sin servidores que instalar, parchear ni escalar.