La seguridad en MQTT se reduce a un puñado de hábitos que la mayoría de los equipos conoce y que, aun así, se salta cuando aprietan los plazos. Esta guía los convierte en una lista de 12 puntos que puedes aplicar a cualquier despliegue, con código listo para copiar que verifica TLS en Python y en la línea de comandos. En cada punto indicamos también qué hace ya por ti un broker MQTT gestionado como ConnectMQ y qué sigue siendo tarea tuya.
Por qué la seguridad en MQTT necesita su propia lista
MQTT se diseñó para ser ligero. El propio protocolo define un campo de usuario y contraseña en el CONNECT y deja todo lo demás (cifrado, autorización, protección frente a abusos) en manos del broker y de la capa de red. Esa flexibilidad es estupenda para dispositivos con recursos limitados, pero también significa que un despliegue MQTT es exactamente tan seguro como su decisión de configuración más débil.
Los fallos típicos son aburridos y evitables:
- Dispositivos que hablan TCP sin cifrar en el puerto 1883 a través de internet.
- Una única contraseña compartida grabada en el firmware de todos los dispositivos.
- Topics como
customers/jane.doe@example.com/locationque filtran datos solo con el nombre del topic. - Suscriptores que confían en cualquier payload y se caen (o algo peor) ante una entrada malformada.
- Clientes que «arreglan» un error de TLS desactivando la verificación de certificados.
Si no conoces todavía cómo enrutan los mensajes los brokers, empieza por ¿Qué es un broker MQTT? y vuelve aquí.
La lista de seguridad MQTT de un vistazo
| # | Práctica | Tu responsabilidad | Qué ofrece ConnectMQ |
|---|---|---|---|
| 1 | Usa siempre TLS | Conéctate en el 8883 / 8084 | TLS en el 8883, WSS en el 8084, certificado de Let's Encrypt |
| 2 | Mantén los datos reales fuera del área pública | Usa credenciales del portal en producción | Topics del inquilino aislados del área pública |
| 3 | Credenciales únicas por broker/dispositivo | No reutilices una contraseña en todas partes | Usuario, contraseña y client ID generados por broker |
| 4 | Restringe los client IDs | Usa el ID emitido y sufijos | Solo se aceptan el client ID emitido y sus variantes _suffix |
| 5 | Guarda los secretos de forma segura en los dispositivos | Almacenamiento cifrado, sin secretos en Git | — |
| 6 | Diseña topics que no filtren nada | Sin datos personales en los nombres de topic | Aislamiento multiinquilino |
| 7 | Valida cada payload | Comprobación de esquema en los suscriptores | Límites de tamaño de mensaje por plan |
| 8 | Aplica límites de tasa y cuotas | Limita a los publicadores | Cuotas por plan, reason code 0x97 Quota exceeded |
| 9 | Defiéndete de la fuerza bruta y las avalanchas | Espera progresivamente tras los fallos | Bloqueo de IP tras credenciales erróneas repetidas, protección contra avalanchas de conexiones |
| 10 | Monitoriza el estado de las conexiones | Alerta ante desconexiones inesperadas | $SYS/broker/connection/{clientId}/state |
| 11 | Valida los certificados en los clientes | Nunca desactives la verificación | Cadena de certificados de confianza pública |
| 12 | Autoriza a nivel de topic | Separa brokers o aplicaciones por perímetro de confianza | Aislamiento a nivel de inquilino |
El resto del artículo repasa cada punto.
1. Usa siempre TLS (puertos 8883 y 8084)
MQTT sin cifrar en el puerto 1883 envía usuarios, contraseñas y payloads en texto plano. Cualquiera que esté en el camino (el Wi-Fi de una cafetería, un router comprometido, un puerto de switch en espejo) puede leerlos y reenviarlos. TLS lo soluciona cifrando la conexión y demostrando la identidad del broker.
En ConnectMQ, todos los brokers aceptan:
- MQTT sobre TLS en
mqtts://broker.connectmq.com:8883 - MQTT sobre WebSocket seguro en
wss://broker.connectmq.com:8084/mqtt
Ambos usan un certificado de Let's Encrypt, así que los almacenes de confianza estándar de los sistemas operativos ya confían en él. Considera los puertos 1883 y 8083 solo para desarrollo. Las aplicaciones de navegador tienen que usar WSS de todos modos; consulta MQTT sobre WebSocket para ver un ejemplo completo.
2. Nunca pongas datos reales en el área pública
broker.connectmq.com, el servidor MQTT público gratuito de ConnectMQ, ofrece un área pública anónima para pruebas y experimentos, no para datos de producción: te conectas sin usuario, con un client ID único, y puedes publicar y suscribirte al instante. Es fantástica para aprender y es completamente pública. Cualquiera que se suscriba al topic adecuado puede leer tus mensajes.
Reglas para el área pública:
- Usa un prefijo aleatorio como
connectmq/demo/<your-random-id>/...para no colisionar con otros. - Nunca publiques datos personales, credenciales, ubicaciones ni nada que no publicarías en un foro.
- Pasa a credenciales del portal antes de que intervengan usuarios o dispositivos reales.
Los inquilinos autenticados en ConnectMQ están aislados: sus topics no son visibles para otros inquilinos ni para el área pública, y los clientes públicos solo ven a otros clientes públicos. Tienes más detalles en la guía del servidor MQTT público gratuito enlazada más arriba.
3. Usa credenciales únicas por broker y dispositivo
Una sola contraseña compartida por toda una flota significa que una imagen de firmware extraída compromete todos los dispositivos. Busca credenciales que puedan revocarse sin tocar el resto de la flota.
En el portal de ConnectMQ (https://app.connectmq.com), cada broker que creas recibe un usuario, una contraseña y un client ID generados. Los secretos generados evitan las clásicas elecciones débiles (admin/admin, iot123) y mantienen separados los entornos: crea un broker para staging y otro para producción en lugar de compartir credenciales entre ellos.
Sea cual sea el broker, rota las credenciales cuando se retire un dispositivo y nunca pegues secretos de producción en tickets, chats o capturas de pantalla.
4. Restringe los client IDs
El client ID de MQTT identifica una sesión. Si cualquiera puede conectarse con cualquier client ID, un atacante que conozca el ID de un dispositivo puede dejarlo fuera de línea (el broker desconecta la sesión existente cuando se conecta un ID duplicado) o secuestrar su sesión persistente.
ConnectMQ restringe las conexiones autenticadas al client ID emitido para el broker y a sus variantes _suffix. Si tu ID emitido es YOUR_CLIENT_ID, puedes conectar YOUR_CLIENT_ID_sensor1, YOUR_CLIENT_ID_dashboard, etc., pero no IDs arbitrarios. Así obtienes nombres de sesión predecibles y auditables e impides que desconocidos suplanten tus sesiones.
Consejo práctico: deriva el sufijo de algo estable en el dispositivo, como su número de serie o su dirección MAC, para que las reconexiones reutilicen la misma sesión.
5. Guarda los secretos de forma segura en los dispositivos
Las credenciales tienen que vivir en algún lugar del dispositivo. Haz que ese lugar sea tan difícil de alcanzar como sea razonable:
- No subas credenciales al control de versiones. Usa una cabecera de configuración o un archivo de entorno separado e ignorado por git.
- Usa el almacenamiento protegido de la plataforma cuando exista (flash cifrada / cifrado de NVS en ESP32, un TPM o un elemento seguro en gateways, un gestor de secretos en servidores).
- Desactiva los puertos de depuración y las consolas serie en el firmware de producción para que los secretos no puedan extraerse fácilmente.
- Planifica un paso de aprovisionamiento que escriba secretos por dispositivo durante la fabricación, en lugar de compilarlos en una imagen compartida.
6. Diseña topics que no filtren información
Los nombres de topic son metadatos, y los metadatos filtran información. Aunque los payloads vayan cifrados, un topic como hospital/ward3/patient-4471/heart-rate revela mucho a cualquiera que pueda observarlo.
Pautas:
- Usa identificadores opacos (
site-07/dev-3fa9/telemetry) en lugar de nombres, correos electrónicos o direcciones. - Mantén una jerarquía coherente, por ejemplo
site/area/line/device/kind, para que los permisos y los comodines sigan siendo manejables. - Evita suscripciones con comodines amplios como
#en los consumidores de producción; suscríbete a lo que necesites. - Mantén un número de niveles moderado. El área pública, por ejemplo, permite como máximo 8 niveles de topic.
7. Valida cada payload
El broker mueve bytes; no sabe si tu JSON está bien formado ni si una temperatura de 9.000 °C es plausible. Cada suscriptor debe tratar los payloads entrantes como entradas no fiables.
import json
ALLOWED_KEYS = {"temperature", "humidity", "ts"}
def parse_telemetry(raw: bytes):
if len(raw) > 4096:
raise ValueError("payload too large")
data = json.loads(raw.decode("utf-8"))
if not isinstance(data, dict) or not set(data) <= ALLOWED_KEYS:
raise ValueError("unexpected fields")
t = float(data["temperature"])
if not -40.0 <= t <= 125.0:
raise ValueError("temperature out of range")
return data
Rechaza, registra y sigue adelante. Nunca pases el contenido de un payload directamente a SQL, a comandos de shell o a HTML. ConnectMQ aplica tamaños máximos de mensaje por plan (por ejemplo, 256 KB en el plan Serverless gratuito y 64 KB en el área pública), lo que acota el peor caso, pero la validación del esquema sigue siendo cosa tuya.
8. Aplica límites de tasa y cuotas
Un bucle de firmware con errores que publica tan rápido como puede es un ataque de denegación de servicio que has escrito tú mismo. Limita en el dispositivo (publica cuando haya cambios o a intervalos fijos) y cuenta con que el broker también aplique límites.
ConnectMQ aplica cuotas por plan y por área. Por ejemplo, el área pública permite 10 mensajes por segundo y 10 suscripciones por cliente. Cuando un cliente MQTT 5 supera una cuota, el broker responde con el reason code 0x97 Quota exceeded; bajo carga intensa puede responder con 0x89 Server busy. Gestiona estos códigos de forma explícita: espera, registra y alerta en lugar de reintentar en un bucle cerrado. La guía de características de MQTT 5 explica los reason codes con más detalle.
9. Protégete frente a la fuerza bruta y las avalanchas de conexiones
Los brokers expuestos sufren constantes intentos de adivinar contraseñas y tormentas de reconexión. En ConnectMQ:
- Las direcciones IP se bloquean tras intentos repetidos con credenciales erróneas.
- Las avalanchas de conexiones provocan bloqueos temporales.
- La protección adaptativa frente a sobrecarga mantiene el clúster operativo durante los picos.
En el lado del cliente, implementa un backoff exponencial con jitter. Si toda una flota se reconecta en el mismo instante tras un corte de red, incluso los dispositivos legítimos parecen una avalancha.
10. Monitoriza el estado de las conexiones con $SYS
No puedes proteger lo que no ves. ConnectMQ publica el estado de conexión de cada cliente en $SYS/broker/connection/{clientId}/state: 1 cuando el cliente se conecta y 0 cuando se desconecta. Suscríbete desde un servicio de monitorización y alerta ante caídas inesperadas o ante un dispositivo que se conecta a horas extrañas.
mosquitto_sub -h broker.connectmq.com -p 1883 -i monitor-7c2e \
-t '$SYS/broker/connection/my-device-7c2e/state' -v
# $SYS/broker/connection/my-device-7c2e/state 1
# $SYS/broker/connection/my-device-7c2e/state 0
Combínalo con mensajes Last Will de MQTT para que el resto de tu sistema se entere de las desconexiones no limpias.
11. Valida los certificados en todos los clientes
TLS sin validación de certificados es cifrar hacia un interlocutor desconocido. Un atacante intermedio puede presentar cualquier certificado y el cliente le enviará las credenciales tan tranquilo. Por desgracia, «desactivar la verificación» es un apaño muy copiado para los errores de TLS.
Python (paho-mqtt 2.x con el módulo ssl)
import ssl
import paho.mqtt.client as mqtt
def on_connect(client, userdata, flags, reason_code, properties):
print("Connected:", reason_code)
ctx = ssl.create_default_context() # CA del sistema, comprobación de hostname activada
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="YOUR_CLIENT_ID")
client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.tls_set_context(ctx)
client.on_connect = on_connect
client.connect("broker.connectmq.com", 8883)
client.loop_forever()
Nunca entregues código con ctx.check_hostname = False, ssl.CERT_NONE o client.tls_insecure_set(True).
mosquitto_pub y mosquitto_sub
En Linux, apunta la CLI al directorio de CA del sistema. En cualquier plataforma puedes, en su lugar, descargar el certificado ISRG Root X1 (isrgrootx1.pem) de Let's Encrypt y pasarlo con --cafile:
# Linux: almacén de CA del sistema
mosquitto_sub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-u YOUR_USERNAME -P YOUR_PASSWORD -i YOUR_CLIENT_ID_cli \
-t "site-07/#" -v
# Cualquier SO: certificado raíz explícito
mosquitto_pub -h broker.connectmq.com -p 8883 --cafile isrgrootx1.pem \
-u YOUR_USERNAME -P YOUR_PASSWORD -i YOUR_CLIENT_ID_pub \
-t "site-07/dev-3fa9/telemetry" -m '{"temperature":22.5}'
Si el hostname o la cadena no coinciden, la conexión falla. Ese fallo es precisamente la función.
12. Autoriza a nivel de topic donde lo necesites
Como práctica general en MQTT, el principio de mínimo privilegio se aplica a los topics: un sensor de temperatura solo debería publicar en su propio topic de telemetría y suscribirse únicamente a su propio topic de comandos. Muchas arquitecturas lo imponen con reglas de autorización a nivel de topic o situando los dispositivos con distintos niveles de confianza detrás de credenciales y brokers separados.
Con ConnectMQ, el perímetro de aislamiento es el inquilino: los topics de un inquilino nunca son visibles para otro inquilino ni para el área pública. Si necesitas una separación estricta entre grupos de dispositivos, crea brokers separados para ellos en el portal y mantén en tu backend las comprobaciones a nivel de aplicación (quién puede enviar qué comando).
Una conexión segura de extremo a extremo
En resumen, un cliente de ConnectMQ listo para producción se conecta en el puerto 8883 (o WSS 8084) con la validación de certificados activada, se autentica con credenciales generadas en el portal y un client ID YOUR_CLIENT_ID_<device>, publica en topics opacos a un ritmo razonable, espera ante 0x97 y 0x89 y se monitoriza mediante $SYS.
Para un recorrido desde el lado del dispositivo, consulta el tutorial de ESP32 con MQTT, que termina con una versión del sketch protegida con TLS.
Empieza con ConnectMQ
ConnectMQ te ofrece de serie endpoints TLS y WSS, credenciales generadas, restricciones de client ID, aislamiento entre inquilinos y protección frente a abusos, para que tu equipo pueda centrarse en la parte de la seguridad MQTT que solo tu equipo puede asumir. Crea un broker con el plan Serverless gratuito en https://app.connectmq.com, sin tarjeta de crédito, y conecta tu primer cliente seguro en minutos.
Preguntas frecuentes
¿Es MQTT seguro por defecto?
No. El protocolo MQTT define campos de autenticación, pero deja el cifrado y la autorización en manos del broker y del transporte. Necesitas TLS, credenciales robustas por dispositivo y límites razonables en el broker para que sea seguro.
¿Qué puerto debo usar para MQTT seguro?
Usa el 8883 para MQTT sobre TLS y el 8084 para MQTT sobre WebSocket seguro (WSS). En ConnectMQ ambos usan un certificado de Let's Encrypt de confianza pública.
¿Puedo usar el área pública de broker.connectmq.com en producción?
No. El área pública es anónima y cualquiera que se suscriba a tus topics puede leerla. Usa credenciales del portal de ConnectMQ para cualquier dato real; los inquilinos autenticados están aislados del área pública y entre sí.
¿Qué significa el reason code 0x97?
0x97 es el reason code de MQTT 5 para «Quota exceeded» (cuota superada). ConnectMQ lo devuelve cuando un cliente supera un límite del plan o del área; tu cliente debería esperar en lugar de reintentar de inmediato.
¿Se puede desactivar la verificación de certificados para hacer pruebas?
Solo en una máquina de desarrollo desechable, y nunca en firmware distribuido ni en código de producción. Desactivar la verificación elimina la protección frente a ataques de intermediario (man-in-the-middle), que es la razón principal para usar TLS.



