Una configuración TLS correcta en MQTT es lo que convierte MQTT de un protocolo en texto plano en uno que puedes usar con seguridad a través de internet. En esta guía aprenderás qué verifica realmente el cliente durante el handshake, qué ajustes importan y cómo configurar clientes Python, Node.js, ESP32 y de línea de comandos para conectarse por el puerto 8883. Todos los ejemplos se conectan a un broker real, así que puedes probar mientras lees.
Por qué MQTT necesita TLS
MQTT en sí no tiene cifrado. En el puerto 1883, el paquete CONNECT, incluidos el usuario y la contraseña, viaja como bytes legibles, igual que cada payload de PUBLISH. Cualquiera en la misma Wi-Fi, un router comprometido o un ISP curioso puede leer e incluso modificar ese tráfico. Si quieres el contexto más amplio de amenazas más allá de las escuchas, lee nuestra guía de buenas prácticas de seguridad MQTT.
TLS (Transport Layer Security) envuelve la conexión TCP en un túnel cifrado y autenticado antes de intercambiar cualquier byte MQTT. MQTT no cambia en absoluto; el broker simplemente escucha en otro puerto, por convención el 8883, y el cliente realiza primero un handshake TLS. Eso te da tres propiedades:
- Confidencialidad: el tráfico está cifrado, así que las credenciales y los payloads no pueden leerse en tránsito.
- Integridad: los paquetes manipulados se detectan y la conexión se corta.
- Autenticación del servidor: el cliente comprueba que habla con el broker real y no con un impostor.
La tercera propiedad es la que más a menudo rompe una configuración descuidada, y es el foco del resto del artículo.
Qué verifica el cliente durante el handshake
Cuando tu cliente abre una conexión TLS, el broker envía su certificado y los certificados intermedios. El cliente realiza entonces estas comprobaciones:
- Cadena de confianza: el certificado del servidor debe estar firmado por un intermedio, que a su vez debe estar firmado por una CA raíz en la que el cliente ya confía (su almacén de CA o un archivo de CA que tú proporcionas).
- Periodo de validez: la hora actual debe estar entre las fechas "not before" y "not after" del certificado. Por eso los dispositivos sin reloj de tiempo real necesitan NTP antes de conectarse.
- Coincidencia del hostname: el nombre de host al que te conectaste debe figurar en la lista Subject Alternative Name del certificado. Conectarse por dirección IP a un certificado emitido para un hostname fallará, y debe fallar.
- Posesión de la clave: el handshake demuestra que el broker tiene la clave privada correspondiente al certificado.
Hay dos detalles relacionados que confunden a mucha gente. Primero, SNI (Server Name Indication): el cliente envía el hostname dentro del handshake para que un servidor que aloja varios nombres pueda presentar el certificado correcto. Las librerías modernas envían SNI automáticamente cuando pasas un hostname. Segundo, en algunas librerías la verificación del hostname es independiente de la verificación de la cadena. Un cliente puede aceptar un certificado perfectamente válido para el dominio equivocado si la comprobación del hostname está desactivada, lo que anula el sentido de TLS.
Ajustes de configuración TLS en MQTT de un vistazo
La tabla relaciona los transportes habituales con sus puertos y esquemas de URL. Los puertos indicados son los que usa ConnectMQ y la mayoría de los brokers que siguen la convención.
| Transporte | Puerto | Esquema de URL | Cifrado | Cliente típico |
|---|---|---|---|---|
| MQTT sobre TCP | 1883 | mqtt:// |
No | Solo pruebas de laboratorio |
| MQTT sobre TLS | 8883 | mqtts:// |
Sí | Dispositivos, backends, CLI |
| MQTT sobre WebSocket | 8083 | ws:// |
No | Pruebas locales en navegador |
| MQTT sobre WebSocket seguro | 8084 | wss:// (ruta /mqtt) |
Sí | Navegadores, redes restrictivas |
Para el ancla de confianza tienes tres opciones:
- Almacén de CA del sistema: la opción por defecto en Python, Node.js, Go y la mayoría de los clientes de escritorio o servidor. Funciona cuando el broker usa un certificado de confianza pública.
- Archivo de CA explícito: pasas un archivo PEM con la CA raíz. Necesario para CA privadas y para la mayoría de los microcontroladores.
- Pinning: confías solo en una CA o clave concreta. Más fuerte pero más frágil; consulta nuestra guía de certificate pinning en ESP32.
El broker de ConnectMQ usa un certificado de Let's Encrypt, que encadena con la raíz ISRG Root X1 incluida en todos los sistemas operativos habituales y en los paquetes de CA de Python (a través de certifi) y Node.js. En escritorios y servidores normalmente no necesitas proporcionar ningún archivo de CA.
Probar la conexión antes de escribir código
Antes de tocar el código de la aplicación, confirma la ruta de red y el certificado con OpenSSL:
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -brief </dev/null
Busca Verification: OK y la versión del protocolo negociada. Si aquí ves un error de verificación, ninguna librería cliente funcionará tampoco; nuestro artículo sobre cómo solucionar fallos del handshake TLS en MQTT repasa cada error.
Después prueba un intercambio MQTT real con los clientes de mosquitto. Abre dos terminales y elige un prefijo de topic único:
# Terminal 1: suscripción por TLS usando el directorio de CA del sistema (Linux/macOS)
mosquitto_sub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-i tls-sub-7f3a -t "connectmq/demo/7f3a/#" -v
# Terminal 2: publicación por TLS
mosquitto_pub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-i tls-pub-7f3a -t "connectmq/demo/7f3a/hello" -m "encrypted hello"
En Windows, mosquitto no puede leer el almacén del sistema, así que descarga el PEM de ISRG Root X1 desde Let's Encrypt y pásalo con --cafile isrgrootx1.pem. El servidor MQTT público gratuito acepta conexiones anónimas, por lo que estos comandos funcionan de inmediato. Recuerda que el área pública la puede leer cualquiera, así que nunca envíes datos reales allí.
Configuración del cliente Python (paho-mqtt 2.x)
Con paho-mqtt 2.x, tls_set() sin argumentos carga los certificados de CA por defecto del sistema, activa la comprobación del hostname y exige un certificado válido. Es justo lo que quieres.
import ssl
import time
import paho.mqtt.client as mqtt
BROKER = "broker.connectmq.com"
PORT = 8883
TOPIC = "connectmq/demo/7f3a/python"
def on_connect(client, userdata, flags, reason_code, properties):
print("Connected:", reason_code)
client.subscribe(TOPIC, qos=1)
def on_message(client, userdata, msg):
print(f"{msg.topic}: {msg.payload.decode()}")
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="py-tls-7f3a")
client.on_connect = on_connect
client.on_message = on_message
# Almacén de CA del sistema, certificado obligatorio, hostname verificado,
# la versión de TLS más reciente que admitan ambos lados
client.tls_set(cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLS_CLIENT)
# Para un broker privado de ConnectMQ, añade las credenciales del portal:
# client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.connect(BROKER, PORT, keepalive=60)
client.loop_start()
time.sleep(1)
client.publish(TOPIC, "hello over TLS", qos=1)
time.sleep(2)
client.loop_stop()
client.disconnect()
Si el almacén de CA de tu plataforma está roto o desactualizado, pasa ca_certs=certifi.where() en su lugar. Nunca "arregles" un error con tls_insecure_set(True); eso desactiva la comprobación del hostname y elimina en silencio la autenticación del servidor. Para los errores habituales en Python, consulta SSL CERTIFICATE_VERIFY_FAILED en MQTT.
Configuración del cliente Node.js (MQTT.js v5)
MQTT.js usa la pila TLS integrada de Node, que valida contra la lista de CA de Mozilla incluida en Node y verifica el hostname por defecto.
const mqtt = require("mqtt");
const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
clientId: "node-tls-7f3a",
protocolVersion: 5,
// Para un broker privado:
// username: "YOUR_USERNAME",
// password: "YOUR_PASSWORD",
rejectUnauthorized: true, // valor por defecto; nunca lo pongas a false en producción
});
const topic = "connectmq/demo/7f3a/node";
client.on("connect", () => {
console.log("Connected over TLS");
client.subscribe(topic, { qos: 1 }, () => {
client.publish(topic, "hello from Node.js", { qos: 1 });
});
});
client.on("message", (t, payload) => {
console.log(`${t}: ${payload.toString()}`);
client.end();
});
client.on("error", (err) => console.error("MQTT error:", err.message));
Si tienes que confiar en una CA privada, añade ca: fs.readFileSync("ca.pem") a las opciones. En el navegador, usa wss://broker.connectmq.com:8084/mqtt; el navegador se encarga de validar el certificado.
Configuración del cliente ESP32
Los microcontroladores no tienen un almacén de CA del sistema operativo, así que debes incrustar el certificado raíz. Con el core de Arduino, WiFiClientSecure más PubSubClient queda así:
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <time.h>
const char* WIFI_SSID = "your-ssid";
const char* WIFI_PASS = "your-password";
const char* BROKER = "broker.connectmq.com";
// Pega aquí el PEM completo de ISRG Root X1 desde letsencrypt.org
static const char ROOT_CA[] = R"EOF(
-----BEGIN CERTIFICATE-----
...ISRG Root X1 certificate body...
-----END CERTIFICATE-----
)EOF";
WiFiClientSecure net;
PubSubClient mqtt(net);
void setup() {
Serial.begin(115200);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) delay(250);
// Se necesita un reloj válido para comprobar las fechas del certificado
configTime(0, 0, "pool.ntp.org");
while (time(nullptr) < 1700000000) delay(250);
net.setCACert(ROOT_CA);
mqtt.setServer(BROKER, 8883);
}
void loop() {
if (!mqtt.connected()) {
if (mqtt.connect("esp32-tls-7f3a")) {
mqtt.publish("connectmq/demo/7f3a/esp32", "hello over TLS");
} else {
Serial.printf("MQTT connect failed, state=%d\n", mqtt.state());
delay(5000);
}
}
mqtt.loop();
}
Para un broker privado usa mqtt.connect("YOUR_CLIENT_ID", "YOUR_USERNAME", "YOUR_PASSWORD"). El tutorial de MQTT sobre SSL con ESP32 trata con más detalle el uso de memoria y el comportamiento de reconexión.
Checklist para producción
- Usa el puerto 8883 (o el 8084 para navegadores) en todas partes fuera del laboratorio; desactiva o bloquea con el firewall el 1883 en claro para los clientes de producción.
- Verifica la cadena de certificados contra una raíz de confianza, nada de "aceptar cualquier cosa".
- Mantén activada la verificación del hostname y conéctate siempre por hostname, nunca por IP.
- Sincroniza la hora (NTP) en los dispositivos embebidos antes del handshake.
- Confía en la CA raíz, no en el certificado hoja, porque los certificados hoja rotan a menudo.
- Envía las credenciales solo sobre TLS y guárdalas fuera del código fuente.
- Prepárate para cambios de CA: haz que la raíz de confianza pueda actualizarse por OTA.
- Monitoriza las conexiones fallidas; los fallos repetidos de TLS o de autenticación suelen indicar un error de configuración o un ataque.
Deja que un broker MQTT gestionado se ocupe del lado del servidor
El lado del broker en TLS es la mitad menos divertida: obtener certificados, renovarlos antes de que caduquen, elegir versiones de protocolo y cifrados y reiniciar listeners sin desconectar a los clientes. Un broker MQTT gestionado elimina ese trabajo. ConnectMQ termina TLS en el 8883 y WebSocket seguro en el 8084 con certificados de Let's Encrypt renovados automáticamente, así que tus clientes solo necesitan la configuración estándar mostrada arriba. Si estás comparando opciones de alojamiento, nuestra guía de broker MQTT en la nube explica las ventajas e inconvenientes.
Empieza con ConnectMQ
Crea una cuenta gratuita en app.connectmq.com, elige el plan Serverless (Free) sin tarjeta de crédito, crea un broker y copia el usuario, la contraseña y el client ID generados. Apunta cualquiera de los clientes anteriores al puerto 8883 con esas credenciales y tendrás una conexión MQTT cifrada y autenticada en minutos.
Preguntas frecuentes
¿Es MQTT sobre TLS más lento que MQTT en claro?
El handshake añade viajes de ida y vuelta y trabajo de CPU al abrir una conexión, pero las conexiones MQTT son de larga duración, así que la sobrecarga en régimen estable es pequeña. Mantén las conexiones abiertas y reutiliza sesiones en lugar de reconectar para cada mensaje.
¿Necesito un certificado de cliente para usar MQTT sobre TLS?
No. La mayoría de los despliegues usan TLS en el lado del servidor más un usuario y una contraseña enviados dentro del túnel cifrado. Los certificados de cliente (TLS mutuo) son una opción adicional que admiten algunos brokers autoalojados.
¿Qué certificado de CA necesito para ConnectMQ?
El certificado del broker lo emite Let's Encrypt y encadena con ISRG Root X1. Los clientes de escritorio y servidor ya confían en él; los dispositivos embebidos deben incrustar ese PEM raíz.
¿Por qué falla mi cliente cuando me conecto por dirección IP?
El certificado enumera hostnames, no direcciones IP, así que la verificación del hostname falla. Conéctate siempre usando broker.connectmq.com o el hostname de tu broker.



