Sécurité MQTT

Authentification JWT MQTT : des jetons dans le champ mot de passe

Une explication concrète du transport des JSON Web Tokens dans le CONNECT MQTT, de leur validation par le broker, de la gestion de l'expiration et du renouvellement sur les appareils, et des cas où des identifiants statiques par appareil restent le choix le plus simple.

Schéma de l'authentification JWT MQTT montrant un émetteur de jetons, un appareil et un broker qui valide un jeton signé
Essayez-le sur votre propre broker MQTTObtenez un broker MQTT privé et managé avec l’offre Free et partez d’exemples de code prêts à l’emploi.
Essayer gratuitement Exemples de code Aucune carte bancaire requise

L'authentification JWT MQTT consiste, pour un client, à placer un JSON Web Token signé dans le champ mot de passe de son paquet CONNECT ; le broker valide alors la signature et les claims au lieu de comparer un mot de passe stocké. Cet article explique ce qui se passe sur le réseau, comment gérer l'expiration et le renouvellement des jetons sur de vrais appareils, et dans quels cas cette complexité supplémentaire vaut la peine par rapport à des identifiants statiques par appareil.

Comment un JWT voyage dans le CONNECT MQTT

L'authentification de base de MQTT ne prévoit aucun champ dédié aux jetons. Elle dispose d'une chaîne username et d'un champ password que la spécification définit comme des données binaires. Les brokers compatibles JWT réutilisent ce champ :

CONNECT
  Client ID:  sensor-0042
  Username:   sensor-0042            (often ignored or matched to a claim)
  Password:   eyJhbGciOiJSUzI1NiIs...  (the encoded JWT)

Un JWT comporte trois parties encodées en base64url et séparées par des points : un en-tête qui indique l'algorithme, un payload de claims et une signature. Les claims d'un jeton d'appareil typique ressemblent à ceci :

{
  "iss": "https://auth.example.com",
  "sub": "sensor-0042",
  "aud": "mqtt.example.com",
  "iat": 1792130400,
  "exp": 1792134000,
  "topics": {
    "pub": ["plant1/line3/sensor-0042/#"],
    "sub": ["plant1/line3/sensor-0042/cmd/#"]
  }
}

Le broker, ou un plugin d'authentification qui lui est rattaché, procède ensuite ainsi :

  1. Il décode le jeton et vérifie la signature avec un secret partagé (HS256) ou une clé publique (RS256, ES256).
  2. Il contrôle exp, nbf, iss et aud.
  3. Il compare éventuellement sub au client ID ou au username.
  4. Il lit éventuellement des claims personnalisés pour décider quels topics le client peut utiliser.

Comme le jeton circule en tant que mot de passe, la même règle s'applique qu'à toute authentification MQTT par nom d'utilisateur et mot de passe : il doit être envoyé via TLS. Un JWT est un identifiant au porteur. Quiconque l'intercepte peut l'utiliser jusqu'à son expiration.

L'authentification étendue de MQTT 5

MQTT 5.0 ajoute une propriété Authentication Method et un paquet AUTH, afin que le broker et le client puissent mener des échanges de type défi-réponse et se réauthentifier pendant une session active sans déconnexion. Certains brokers proposent des méthodes à jeton via ce mécanisme, ce qui permet de renouveler un jeton sur une connexion ouverte. La prise en charge varie beaucoup d'un broker et d'une bibliothèque client à l'autre ; la plupart des déploiements utilisent donc toujours l'approche plus simple du champ mot de passe et se reconnectent pour renouveler le jeton. La spécification OASIS MQTT 5.0 décrit le flux AUTH.

Quels brokers prennent en charge JWT ?

JWT est une fonctionnalité du broker ou d'un plugin, pas de MQTT lui-même. Certains brokers intègrent un authentificateur JWT, et Mosquitto peut valider des jetons via des plugins d'authentification tiers. Consultez la documentation de votre broker pour savoir quels algorithmes, quelles sources de clés (clé statique ou endpoint JWKS) et quels mappages de claims il prend en charge.

ConnectMQ ne prend pas en charge l'authentification JWT. Un broker ConnectMQ authentifie les clients avec le nom d'utilisateur et le mot de passe générés dans le portail, et lie chaque client ID aux identifiants qui lui ont été attribués. Les tenants sont isolés les uns des autres, si bien que ces identifiants ne donnent accès qu'à votre propre broker. Les compromis présentés ci-dessous vous aideront à déterminer si ce modèle ou un modèle à jetons convient à votre projet.

Émettre et utiliser un jeton : exemple

L'exemple ci-dessous montre les deux moitiés d'une configuration JWT face à un broker hypothétique sur mqtt.example.com qui valide des jetons RS256. D'abord, un service émetteur minimal signe un jeton pour un appareil :

# pip install pyjwt cryptography
import time
import jwt

PRIVATE_KEY = open("issuer_private.pem", "rb").read()

def issue_device_token(device_id: str, ttl_seconds: int = 3600) -> str:
    now = int(time.time())
    claims = {
        "iss": "https://auth.example.com",
        "sub": device_id,
        "aud": "mqtt.example.com",
        "iat": now,
        "nbf": now - 30,          # tolérer un léger décalage d'horloge
        "exp": now + ttl_seconds,
    }
    return jwt.encode(claims, PRIVATE_KEY, algorithm="RS256")

if __name__ == "__main__":
    print(issue_device_token("sensor-0042"))

Ensuite, l'appareil se connecte avec le jeton comme mot de passe et se reconnecte avec un jeton neuf avant l'expiration :

# pip install paho-mqtt requests
import ssl
import time
import requests
import paho.mqtt.client as mqtt

DEVICE_ID = "sensor-0042"
TOKEN_URL = "https://auth.example.com/device-token"
REFRESH_EVERY = 50 * 60   # renouveler 10 minutes avant une expiration à 60 minutes

def fetch_token() -> str:
    # L'appareil s'authentifie auprès de l'émetteur avec son propre secret à long terme
    r = requests.post(TOKEN_URL, json={"device": DEVICE_ID}, timeout=10)
    r.raise_for_status()
    return r.json()["token"]

def on_connect(client, userdata, flags, reason_code, properties):
    print("CONNACK:", reason_code)

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
                     client_id=DEVICE_ID, protocol=mqtt.MQTTv5)
client.tls_set(cert_reqs=ssl.CERT_REQUIRED)
client.on_connect = on_connect

def connect_with_fresh_token():
    client.username_pw_set(DEVICE_ID, fetch_token())
    client.connect("mqtt.example.com", 8883, keepalive=60)
    client.loop_start()

connect_with_fresh_token()
while True:
    time.sleep(REFRESH_EVERY)
    client.disconnect()      # déconnexion propre, session conservée si configurée
    client.loop_stop()
    connect_with_fresh_token()

Notez le commentaire dans fetch_token : l'appareil a toujours besoin d'un secret ou d'un certificat à long terme pour prouver son identité auprès de l'émetteur. JWT déplace l'identifiant statique de la connexion au broker vers la connexion à l'émetteur ; il ne le supprime pas.

Modèles d'expiration et de renouvellement

L'expiration est la principale raison d'utiliser des jetons, et aussi la principale source d'incidents en production. Les brokers gèrent un jeton qui expire de l'une de ces deux façons :

Comportement du broker Effet sur le client
Vérifie exp uniquement au CONNECT La session continue après l'expiration jusqu'à la prochaine reconnexion
Déconnecte le client à exp Le client doit se reconnecter avec un nouveau jeton avant ce moment

Les modèles qui fonctionnent sur les appareils :

  1. Renouveler tôt. Demandez un nouveau jeton vers 80 % de sa durée de vie, pas au moment de l'expiration.
  2. Se reconnecter proprement. Utilisez une session persistante (Clean Start à false avec une session expiry) pour que les abonnements et les messages QoS 1 en file d'attente survivent à la reconnexion.
  3. Garder l'horloge à l'heure. Un appareil dont l'horloge est fausse produira ou rejettera des jetons à tort. Synchronisez l'heure via NTP avant de demander un jeton et tolérez un léger décalage avec nbf.
  4. Gérer les pannes de l'émetteur. Si l'émetteur est indisponible, l'appareil conserve sa connexion actuelle et réessaie avec un backoff ; il ne doit pas abandonner une session qui fonctionne.
  5. Journaliser les codes de CONNACK. Un jeton expiré ou invalide produit généralement 0x86 (Bad User Name or Password) ou 0x87 (Not authorized) en MQTT 5.

Pièges de sécurité des jetons

Les jetons échouent d'une manière différente des mots de passe ; passez donc ces points en revue avant la mise en production :

  • Confusion d'algorithme. Configurez le broker pour n'accepter que l'algorithme que vous utilisez, par exemple RS256. N'acceptez jamais none et ne laissez jamais l'en-tête du jeton choisir entre vérification symétrique et asymétrique.
  • Secrets HMAC partagés sur les appareils. Si les appareils pouvaient signer leurs propres jetons HS256, chaque appareil détiendrait le secret maître. Signez les jetons uniquement côté serveur.
  • Durées de vie trop longues. Un jeton valable un an n'est qu'un mot de passe avec des étapes en plus. Choisissez des durées de vie assez courtes pour qu'un jeton divulgué expire vite, et assez longues pour que les appareils sur des réseaux médiocres ne passent pas leur temps à se renouveler.
  • Absence de contrôle de l'audience. Sans aud, un jeton émis pour un autre service peut être rejoué contre votre broker.
  • Journalisation des jetons. Les jetons finissent dans les journaux de débogage et les rapports de plantage, exactement comme les mots de passe. Journalisez sub et exp, jamais le jeton.

JWT ou identifiants statiques par appareil

Critère JWT Identifiants statiques par appareil
Durée de validité d'un secret volé sur le réseau Jusqu'à l'expiration du jeton Jusqu'à la rotation de l'identifiant
Infrastructure supplémentaire Émetteur de jetons, gestion des clés, synchronisation d'horloge Rien au-delà du broker
Révocation Attendre l'expiration ou maintenir une liste de refus Révoquer l'identifiant
Données d'autorisation Peuvent être portées par des claims de topics dans le jeton Gérées côté broker
Complexité côté appareil Client HTTP, logique de renouvellement, NTP Stocker un mot de passe
Débogage Décoder le jeton, vérifier les claims et les horloges Vérifier le nom d'utilisateur et le mot de passe

JWT est à son avantage lorsque vous exploitez déjà un fournisseur d'identité, lorsque des utilisateurs (et pas seulement des appareils) se connectent depuis des navigateurs ou des applications mobiles avec des sessions courtes, ou lorsque vous voulez intégrer l'autorisation dans l'identifiant lui-même.

Les identifiants statiques par appareil sont à leur avantage pour les parcs d'appareils où la simplicité compte : microcontrôleurs aux ressources limitées, sites dont l'accès à un émetteur est peu fiable, équipes qui ne souhaitent pas exploiter un service d'authentification. Leur faiblesse, des secrets à longue durée de vie, se traite par un stockage protégé et une rotation planifiée. Consultez comment effectuer la rotation des identifiants MQTT et les notes de conception sur les concepts d'ACL MQTT pour le volet autorisation.

Un juste milieu avec un broker MQTT managé

Si ce que vous attendez de JWT, c'est surtout « aucun secret partagé et un moyen rapide de couper un appareil », vous pouvez l'obtenir en grande partie sans jetons : donnez à chaque appareil son propre jeu d'identifiants, liez-le à un client ID, conservez-le dans un stockage protégé et effectuez-en la rotation selon un calendrier. C'est le modèle qu'un broker MQTT managé comme ConnectMQ fournit d'emblée, avec TLS sur les ports 8883 et 8084, le blocage d'IP après des échecs de connexion répétés et l'isolation des tenants. Pour les applications web, conservez les identifiants du broker sur votre backend et laissez les utilisateurs s'authentifier auprès de ce backend avec le système de jetons que vous utilisez déjà.

Démarrer avec ConnectMQ

Vous voulez une authentification solide par appareil sans exploiter d'émetteur de jetons ? Créez gratuitement un broker MQTT cloud Serverless sur app.connectmq.com, sans carte bancaire, et connectez votre premier appareil via TLS avec des identifiants générés en quelques minutes.

Questions fréquentes

ConnectMQ prend-il en charge l'authentification JWT MQTT ?

Non. ConnectMQ authentifie les clients avec des noms d'utilisateur et mots de passe générés, chaque client ID étant lié aux identifiants qui lui ont été attribués. Les tenants sont isolés les uns des autres.

Où se place le JWT dans une connexion MQTT ?

La plupart des brokers l'attendent dans le champ mot de passe du CONNECT, parfois avec le username défini sur l'ID de l'appareil ou sur une valeur fixe. Certains brokers MQTT 5 acceptent aussi les jetons via l'authentification étendue.

Que se passe-t-il si le jeton expire pendant une session ?

Cela dépend du broker. Certains ne valident qu'au moment de la connexion, d'autres déconnectent le client à l'expiration ; les appareils doivent donc renouveler le jeton et se reconnecter avant qu'il n'expire.

JWT est-il plus sûr qu'un mot de passe ?

Un jeton de courte durée limite le temps pendant lequel un identifiant volé reste utilisable, mais les appareils ont toujours besoin d'un secret à long terme pour obtenir des jetons. La sécurité globale dépend de la façon dont ces deux secrets sont stockés et transportés.

Articles liés

Rotation des identifiants MQTT sans interruption de service

Une stratégie à double identifiant, étape par étape, pour la rotation des noms d'utilisateur et mots de passe MQTT sur un parc : préparer le nouvel identifiant, basculer avec repli, vérifier, puis seulement révoquer l'ancien.

8 min de lecture

Tous les articles →

Votre broker MQTT privé, à quelques clics

Créez un compte, récupérez les identifiants de votre broker et connectez votre premier appareil — aucun serveur à installer, mettre à jour ou dimensionner.