Seguridad MQTT

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.

Línea de tiempo que muestra cómo rotar credenciales MQTT con las credenciales antigua y nueva solapándose antes de la revocación
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

Para rotar credenciales MQTT sin tiempo de inactividad necesitas dos conjuntos de credenciales válidos a la vez, dispositivos capaces de volver de la nueva a la antigua y un paso de verificación antes de revocar nada. Esta guía te ofrece esa estrategia de doble credencial paso a paso, con código para el dispositivo y el orden de operaciones para emitir y revocar credenciales en un broker MQTT gestionado.

¿Por qué rotar las credenciales MQTT?

Las credenciales estáticas tienen una vida larga en los dispositivos. Con los años pasan por pipelines de compilación, portátiles de soporte, hardware devuelto y traspasos entre contratistas. La rotación limita cuánto tiempo sigue siendo útil cualquier copia filtrada. También necesitas un proceso de rotación probado para los imprevistos:

  • Un dispositivo es robado, devuelto o dado de baja.
  • Se publicó por error una imagen de firmware con secretos incrustados.
  • Se marcha un empleado o integrador con acceso a las credenciales.
  • Una revisión de seguridad o un contrato con un cliente exige rotación periódica.

El peor momento para diseñar un proceso de rotación es en mitad de un incidente. Ejecutarlo de forma programada mantiene su funcionamiento.

Por qué la rotación ingenua provoca caídas

El enfoque tentador es: cambiar la contraseña en el broker y después enviar la nueva a los dispositivos. Todos los dispositivos que todavía no han recibido la actualización se quedan fuera, y un dispositivo sin acceso a MQTT a menudo tampoco puede recibir la actualización, porque el canal de actualización es MQTT. Acabas con una visita in situ por cada dispositivo desconectado.

El orden inverso también falla: si los dispositivos cambian primero y el broker aún no conoce la nueva credencial, no pueden conectarse. El único enfoque seguro es una ventana de solapamiento en la que funcionen ambas credenciales.

La estrategia de rotación con doble credencial

Fase Estado del broker Estado del dispositivo Condición de salida
1. Emitir Antigua y nueva válidas Solo la antigua Nueva credencial creada y registrada
2. Preparar Antigua y nueva válidas Antigua activa, nueva almacenada El dispositivo confirma que ha guardado la nueva
3. Cambiar Antigua y nueva válidas Nueva activa, antigua como respaldo Dispositivo conectado con la nueva credencial
4. Verificar Antigua y nueva válidas Nueva activa Sin conexiones con la credencial antigua durante un periodo fijado
5. Revocar Solo la nueva Solo la nueva, antigua eliminada Credencial antigua eliminada en todas partes

Las fases se aplican a cualquier broker. Lo que cambia es cómo se emiten y revocan las credenciales.

Fase 1: emite la nueva credencial

En un broker Mosquitto autoalojado, esto significa añadir un segundo usuario al archivo de contraseñas. En ConnectMQ, emite un nuevo conjunto de credenciales para el dispositivo en el portal. Cada conjunto incluye su propio nombre de usuario, contraseña y client ID, porque los client IDs están vinculados a las credenciales emitidas. Registra en tu inventario la correspondencia entre el dispositivo físico y los client IDs antiguo y nuevo antes de seguir.

Esa vinculación tiene una consecuencia práctica: durante la rotación el dispositivo no solo cambia su contraseña, sino toda su terna de identidad. Asegúrate de que cualquier consumidor que se base en el client ID, como la supervisión de presencia en $SYS/broker/connection/{clientId}/state o tu propio inventario, conozca ambos IDs durante el solapamiento.

Fase 2: prepárala en el dispositivo

Entrega la nueva credencial a través de la conexión existente, autenticada y protegida con TLS. Un patrón habitual es un topic de comandos que solo escucha ese dispositivo:

devices/<deviceId>/cmd/credentials    # backend -> dispositivo, QoS 1, no retenido
devices/<deviceId>/evt/credentials    # dispositivo -> backend, confirmación

Nunca publiques credenciales como mensajes retenidos, ni en un topic compartido o legible mediante comodines. Si tu broker no aplica permisos por topic, mantén a los clientes no confiables completamente fuera de él, o entrega las credenciales por un canal separado, como tu servicio de actualizaciones OTA. El dispositivo escribe la nueva credencial en un almacenamiento protegido, en una ranura distinta de la actual, y confirma con un hash, no con la contraseña en sí.

Fase 3: cambia con respaldo

El dispositivo se reconecta con la nueva credencial. Si el broker la rechaza, el dispositivo vuelve a la antigua e informa del fallo. Este es el paso que hace segura la rotación.

import json
import os
import ssl
import time
import paho.mqtt.client as mqtt

HOST = "YOUR_BROKER_HOST"
STORE = "/var/lib/mydevice/credentials.json"   # protégelo con permisos 0600

def load():
    with open(STORE) as f:
        return json.load(f)   # {"active": {...}, "pending": {...} or null}

def save(data):
    tmp = STORE + ".tmp"
    with open(tmp, "w") as f:
        json.dump(data, f)
    os.replace(tmp, STORE)    # intercambio atómico, resiste un corte de luz a mitad de escritura

def try_connect(cred, timeout=15):
    result = {}
    def on_connect(client, userdata, flags, reason_code, properties):
        result["rc"] = reason_code
    c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
                    client_id=cred["client_id"], protocol=mqtt.MQTTv5)
    c.username_pw_set(cred["username"], cred["password"])
    c.tls_set(cert_reqs=ssl.CERT_REQUIRED)
    c.on_connect = on_connect
    c.connect(HOST, 8883, keepalive=60)
    c.loop_start()
    deadline = time.time() + timeout
    while "rc" not in result and time.time() < deadline:
        time.sleep(0.2)
    if result.get("rc") is not None and not result["rc"].is_failure:
        return c
    c.loop_stop()
    c.disconnect()
    return None

def connect_with_rotation():
    data = load()
    if data.get("pending"):
        client = try_connect(data["pending"])
        if client:
            data["previous"] = data["active"]   # se conserva como respaldo hasta la revocación
            data["active"], data["pending"] = data["pending"], None
            save(data)
            return client
        print("New credential rejected, falling back")
    client = try_connect(data["active"])
    if client is None and data.get("previous"):
        client = try_connect(data["previous"])
    return client

Conserva la credencial antigua como previous hasta que el backend indique al dispositivo que se ha revocado. Después, elimínala del almacenamiento.

Fase 4: verifica

Antes de revocar nada, confirma que la flota ha hecho el cambio. Señales útiles:

  1. La confirmación de cada dispositivo en su topic evt/credentials.
  2. La presencia del nuevo client ID en $SYS/broker/connection/{clientId}/state y la ausencia del antiguo.
  3. Tu informe de inventario: dispositivos que siguen con la credencial antigua tras la ventana.

Investiga los rezagados de forma individual. Los dispositivos desconectados son habituales; cambiarán la próxima vez que se conecten, siempre que no revoques demasiado pronto.

Fase 5: revoca

Elimina el conjunto de credenciales antiguo en el portal (o borra el usuario y recarga Mosquitto). Después, indica a los dispositivos que eliminen la ranura previous. A partir de ese momento, una copia filtrada de la credencial antigua no sirve para nada.

Lista de comprobación de la rotación

  1. El inventario asigna cada dispositivo a su client ID actual.
  2. Nueva credencial emitida y registrada antes de que cambie ningún dispositivo.
  3. Credenciales entregadas solo sobre TLS, a un topic o canal específico del dispositivo, nunca retenidas.
  4. El dispositivo guarda la nueva credencial en una ranura separada con escritura atómica.
  5. El dispositivo vuelve a la credencial antigua si la nueva es rechazada e informa de ello.
  6. Ventana de solapamiento lo bastante larga para dispositivos que pasan días desconectados.
  7. Revocación solo tras la verificación, y borrado en el dispositivo tras la revocación.
  8. Ejecuta el proceso de forma programada, no solo durante incidentes.

Errores a evitar

  • Bucles de reintento rápidos con una contraseña incorrecta. ConnectMQ bloquea las direcciones IP tras varios inicios de sesión fallidos. Un emplazamiento entero detrás de una misma dirección NAT puede quedar bloqueado por unos pocos dispositivos con mal comportamiento. Aplica backoff exponencial y vuelve rápido a la credencial de respaldo.
  • Dos dispositivos con una misma credencial. Como el client ID está vinculado a la credencial, dos dispositivos que comparten un conjunto se quitarán la sesión mutuamente y se desconectarán en bucle. Cada dispositivo necesita su propio conjunto.
  • Cuota de conexiones durante el solapamiento. Si los dispositivos mantienen brevemente ambas conexiones, o pruebas las nuevas credenciales desde un portátil, cuéntalas dentro del límite de conexiones de tu plan.
  • Olvidar los clientes que no son dispositivos. Los flujos de Node-RED, los agentes de Telegraf y los servicios backend también tienen credenciales. Rótalas con la misma periodicidad.

El almacenamiento en el dispositivo importa tanto como el proceso de rotación. Consulta cómo proteger las credenciales MQTT en los dispositivos para ver opciones en ESP32 y Linux, y el aprovisionamiento seguro de dispositivos IoT para llevar la primera credencial al dispositivo de forma segura.

Empieza con ConnectMQ

Un broker MQTT gestionado te quita de encima los archivos de contraseñas y las renovaciones de certificados para que puedas centrarte en la parte de la rotación que corresponde al dispositivo. Crea un broker Serverless gratuito en app.connectmq.com, sin tarjeta de crédito, y prueba el flujo completo de doble credencial con un par de dispositivos antes de desplegarlo. El servidor MQTT público gratuito no sirve para este ensayo, porque acepta clientes anónimos sin credenciales y está pensado solo para pruebas rápidas, no para datos de producción. Para entender cómo viajan las credenciales en CONNECT, lee autenticación MQTT con usuario y contraseña, y para ver cómo encaja esto en un entorno de producción, lee sobre cómo operar un broker MQTT en la nube.

Preguntas frecuentes

¿Con qué frecuencia debo rotar las credenciales MQTT?

No hay un intervalo universal. Elige uno que tu proceso pueda sostener, normalmente anual o en cada versión importante de firmware, y rota siempre de inmediato tras una sospecha de filtración o cuando un dispositivo deje de estar bajo tu control.

¿Puedo rotar credenciales en dispositivos desconectados?

Sí, siempre que la credencial antigua siga siendo válida hasta que vuelvan. El dispositivo cambia en su siguiente conexión, y tú revocas solo después de que lo haya hecho.

¿Por qué cambia el client ID durante la rotación en ConnectMQ?

ConnectMQ vincula los client IDs a las credenciales emitidas, así que un nuevo conjunto de credenciales trae su propio client ID. Actualiza cualquier supervisión de presencia o inventario que se base en el client ID.

¿Qué pasa si la nueva credencial es rechazada?

El dispositivo debe volver a la credencial antigua, reconectarse e informar del fallo para que puedas corregir la credencial emitida antes de volver a intentarlo.

Artículos relacionados

Listas de control de acceso (ACL) en MQTT: conceptos y diseño

Qué son las listas de control de acceso de MQTT, cómo expresan brokers como Mosquitto las reglas de lectura y escritura por topic, cómo se comportan las operaciones denegadas y cómo diseñar espacios de nombres de topics para que la autorización siga siendo sencilla.

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.