MQTT avec TLS 1.3 raccourcit le handshake, supprime la cryptographie faible et simplifie le choix des suites de chiffrement, mais sur les appareils sur batterie, les vrais coûts viennent d'ailleurs. Cet article explique ce qui change réellement entre TLS 1.2 et 1.3 pour les connexions MQTT, quelles suites de chiffrement autoriser, comment mesurer ce que votre connexion négocie et comment configurer judicieusement des clients Python, Node.js et Mosquitto.
Pourquoi la version de TLS compte pour MQTT
MQTT fonctionne sur une connexion TCP de longue durée. Un appareil se connecte une fois, effectue un handshake TLS, puis échange de petits messages pendant des heures ou des jours. Le coût du handshake est donc rarement payé par les appareils toujours connectés, mais très souvent par ceux qui se mettent en veille et se reconnectent, comme les capteurs sur batterie qui se réveillent toutes les quelques minutes. Pour ces appareils, les allers-retours du handshake et le temps CPU consacré aux opérations à clé publique dominent la consommation d'énergie, et le choix de la version TLS et de la suite de chiffrement compte beaucoup.
Si vous débutez dans la sécurisation des connexions MQTT, commencez par notre guide de configuration TLS MQTT et revenez ici pour les détails de réglage.
TLS 1.2 et TLS 1.3 en un coup d'œil
| Aspect | TLS 1.2 | TLS 1.3 |
|---|---|---|
| Allers-retours d'un handshake complet | 2 avant les données applicatives | 1 avant les données applicatives |
| Handshake repris | 1 aller-retour (session ID ou ticket) | 1 aller-retour (PSK), 0-RTT en option |
| Échange de clés | RSA ou (EC)DHE | (EC)DHE uniquement, toujours avec confidentialité persistante |
| Suites de chiffrement | Des dizaines, beaucoup faibles ou obsolètes | Cinq définies, trois largement utilisées |
| Certificat chiffré pendant le handshake | Non | Oui |
| Mode CBC, RC4, SHA-1, RSA statique | Autorisés (selon la configuration) | Supprimés |
Pour un client sur une liaison cellulaire à forte latence, économiser un aller-retour à chaque connexion se remarque. Pour un appareil sur un réseau local, le gain est faible par rapport au travail cryptographique.
D'où vient réellement le coût du handshake
Sur un microcontrôleur, trois opérations dominent un handshake complet :
- L'échange de clés : un calcul ECDHE, généralement sur X25519 ou la courbe P-256. Il a lieu dans les deux versions de TLS.
- La vérification du certificat : le contrôle des signatures de la chaîne du serveur. La vérification d'une signature RSA est peu coûteuse ; la vérification ECDSA coûte plus cher côté client, mais les certificats ECDSA sont plus petits.
- Les allers-retours réseau : chacun maintient la radio éveillée.
TLS 1.3 réduit les allers-retours mais ne supprime pas les opérations à clé publique. Le gain le plus important pour les appareils qui se reconnectent souvent est généralement la reprise de session, qui évite la vérification du certificat et qui, en mode PSK de TLS 1.3 avec échange de clés, conserve la confidentialité persistante. Vérifiez que votre pile TLS et votre broker la prennent tous deux en charge avant de compter dessus.
L'accélération matérielle compte aussi. La famille ESP32 accélère via mbedTLS l'AES, le SHA et l'arithmétique sur grands nombres utilisée par RSA et ECC, ce qui rend l'une ou l'autre version de TLS praticable. Les microcontrôleurs plus petits, sans accélération, peuvent tirer profit de ChaCha20-Poly1305, rapide en pur logiciel.
Les suites de chiffrement MQTT TLS 1.3
TLS 1.3 sépare la suite de chiffrement (chiffrement symétrique et hachage) des algorithmes d'échange de clés et de signature. Les suites que vous rencontrerez en pratique :
TLS_AES_128_GCM_SHA256: obligatoire à implémenter, rapide avec du matériel AES, un bon choix par défaut.TLS_AES_256_GCM_SHA384: clé plus longue, légèrement plus lente, souvent choisie pour des raisons de politique de sécurité.TLS_CHACHA20_POLY1305_SHA256: excellente sur les processeurs sans accélération AES.
Toutes trois sont des chiffrements authentifiés jugés robustes. Vous aurez rarement besoin de les restreindre. Les deux autres suites définies pour TLS 1.3 (variantes CCM) apparaissent surtout dans les environnements contraints.
Pour TLS 1.2, si vous devez encore le prendre en charge, n'autorisez que les suites AEAD avec confidentialité persistante :
ECDHE-ECDSA-AES128-GCM-SHA256
ECDHE-RSA-AES128-GCM-SHA256
ECDHE-ECDSA-CHACHA20-POLY1305
ECDHE-RSA-CHACHA20-POLY1305
ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-RSA-AES256-GCM-SHA384
Évitez les suites contenant CBC, RC4, 3DES, NULL, EXPORT, ou celles qui utilisent un échange de clés RSA statique (noms commençant par AES sans ECDHE).
Mesurer ce que votre connexion négocie
Ne présumez jamais de la version prise en charge par un broker : mesurez-la. OpenSSL peut forcer chaque version et indiquer le résultat :
# Négocier la meilleure version prise en charge des deux côtés
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -brief </dev/null
# Forcer TLS 1.3
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -tls1_3 -brief </dev/null
# Forcer TLS 1.2
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -tls1_2 -brief </dev/null
Les lignes Protocol version et Ciphersuite montrent le résultat. Lancez les mêmes tests sur tout broker que vous évaluez, et répétez-les après chaque mise à niveau du broker ou des bibliothèques, car les versions prises en charge peuvent évoluer.
Pour chronométrer les handshakes depuis un script, cet extrait Python mesure la latence de connexion plus handshake :
import socket
import ssl
import time
HOST, PORT = "broker.connectmq.com", 8883
def handshake(version: ssl.TLSVersion) -> None:
ctx = ssl.create_default_context()
ctx.minimum_version = version
ctx.maximum_version = version
start = time.perf_counter()
try:
with socket.create_connection((HOST, PORT), timeout=10) as sock:
with ctx.wrap_socket(sock, server_hostname=HOST) as tls:
elapsed = (time.perf_counter() - start) * 1000
print(f"{tls.version()} {tls.cipher()[0]} {elapsed:.1f} ms")
except ssl.SSLError as exc:
print(f"{version.name} not negotiated: {exc.reason}")
for v in (ssl.TLSVersion.TLSv1_2, ssl.TLSVersion.TLSv1_3):
for _ in range(3):
handshake(v)
Lancez-le plusieurs fois ; sur une liaison rapide, la gigue réseau est généralement plus importante que l'écart entre les versions.
Configurer les clients
La politique client la plus sûre est « autoriser TLS 1.2 et plus récent, préférer le plus récent », ce qui laisse le client et le serveur choisir la meilleure version commune.
Python (paho-mqtt 2.x)
import ssl
import paho.mqtt.client as mqtt
ctx = ssl.create_default_context() # vérifie la chaîne et le nom d'hôte
ctx.minimum_version = ssl.TLSVersion.TLSv1_2 # refuser toute version plus ancienne
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="py-tls13-c71e")
client.tls_set_context(ctx)
# client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.connect("broker.connectmq.com", 8883)
client.loop_start()
client.publish("connectmq/demo/c71e/tls", "negotiated securely", qos=1).wait_for_publish()
client.loop_stop()
client.disconnect()
En Python, set_ciphers() n'affecte que TLS 1.2 et les versions antérieures ; les suites TLS 1.3 sont gérées par les valeurs par défaut d'OpenSSL, déjà saines.
Node.js (MQTT.js v5)
const mqtt = require("mqtt");
const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
clientId: "node-tls13-c71e",
protocolVersion: 5,
minVersion: "TLSv1.2", // transmis à tls.connect de Node
});
client.on("connect", () => {
const sock = client.stream;
console.log("TLS:", sock.getProtocol(), sock.getCipher().name);
client.end();
});
Mosquitto comme broker auto-hébergé
Si vous exploitez votre propre broker Mosquitto, vous maîtrisez le côté serveur :
listener 8883
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key
cafile /etc/mosquitto/certs/chain.pem
tls_version tlsv1.2
ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305
ciphers_tls1.3 TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384
Dans Mosquitto, tls_version fixe la version minimale acceptée sur le listener. Restreindre un serveur à TLS 1.3 uniquement est tentant, mais exclut les anciens firmwares et les piles TLS embarquées qui ne prennent encore en charge que TLS 1.2.
Appareils contraints : conseils pratiques
- Gardez les connexions ouvertes. Un handshake par jour coûte moins cher que n'importe quelle optimisation d'un handshake par minute.
- Utilisez la reprise de session lorsque votre pile la prend en charge, pour les appareils qui doivent se mettre en veille.
- Activez la cryptographie matérielle dans la configuration de votre SDK.
- Vérifiez l'état de TLS 1.3 dans votre pile. Les versions récentes de mbedTLS utilisées par ESP-IDF peuvent être compilées avec TLS 1.3 en option de configuration ; les SDK plus anciens ne proposent que TLS 1.2, qui reste sûr avec les suites listées plus haut.
- Ne codez pas en dur une version unique dans le firmware. Autorisez la négociation pour que les appareils continuent de fonctionner quand les serveurs évoluent.
- Évitez le 0-RTT pour les commandes. Les données précoces peuvent être rejouées, ce qui est dangereux pour les commandes d'actionneurs.
Le guide sur l'épinglage de certificat sur ESP32 couvre le volet certificats du TLS embarqué, et la résolution des échecs de handshake TLS MQTT vous aide quand la négociation de version tourne mal.
Checklist de politique de sécurité
Utilisez cette liste lorsque vous rédigez la politique TLS d'un projet MQTT :
- Version minimale du protocole : TLS 1.2 sur les clients, avec TLS 1.3 autorisé et préféré.
- Suites TLS 1.2 : échange de clés ECDHE avec AES-GCM ou ChaCha20-Poly1305 uniquement.
- Suites TLS 1.3 : valeurs par défaut de la bibliothèque, sauf si une politique impose AES-256.
- Vérification des certificats et contrôle du nom d'hôte : toujours activés.
- Stratégie de reconnexion : connexions de longue durée avec backoff, pas une connexion par message.
- Reprise de session : activée lorsque la pile la prend en charge, pour les appareils en veille.
- Revue : relancez les tests de version OpenSSL après chaque mise à niveau du broker, des bibliothèques ou du firmware.
Une politique courte et écrite facilite les audits et empêche des exceptions « temporaires » ponctuelles, comme une vérification de certificat désactivée, d'atteindre la production.
Confiez le réglage du serveur à un broker MQTT managé
Sur un broker auto-hébergé, vous devez suivre vous-même les dépréciations de protocoles, les recommandations de suites de chiffrement et les renouvellements de certificats. Un broker MQTT managé gère le côté serveur à votre place. ConnectMQ fournit TLS sur le port 8883 et le WebSocket sécurisé sur le port 8084 avec un certificat Let's Encrypt : vous n'avez besoin que de la politique client ci-dessus, à savoir vérifier le certificat, autoriser TLS 1.2 et plus récent, et laisser la négociation choisir la meilleure option. Utilisez le serveur MQTT public gratuit, réservé aux tests, pour effectuer les mesures de cet article depuis votre propre réseau.
Démarrer avec ConnectMQ
Inscrivez-vous à ConnectMQ sur app.connectmq.com, choisissez l'offre gratuite Serverless sans carte bancaire et créez un broker. Pointez-y les exemples Python ou Node.js ci-dessus avec vos identifiants générés et vérifiez ce que négocie votre connexion.
Questions fréquentes
TLS 1.2 est-il encore sûr pour MQTT ?
Oui, lorsqu'il est limité à l'échange de clés ECDHE avec des chiffrements AEAD comme AES-GCM ou ChaCha20-Poly1305. Autorisez TLS 1.3 en parallèle pour que les clients l'utilisent lorsqu'il est disponible.
TLS 1.3 réduit-il le surcoût des messages MQTT ?
Pas de manière significative. Le surcoût par enregistrement est similaire ; les principaux gains sont un handshake plus court, des valeurs par défaut plus robustes et des certificats chiffrés.
Quelle suite de chiffrement TLS 1.3 les appareils IoT doivent-ils utiliser ?
TLS_AES_128_GCM_SHA256 sur du matériel doté d'une accélération AES, TLS_CHACHA20_POLY1305_SHA256 sur les processeurs qui en sont dépourvus. Les deux sont robustes.
Faut-il utiliser les données précoces 0-RTT avec MQTT ?
En général, non. Les données précoces peuvent être rejouées par un attaquant, et les clients MQTT y gagnent peu car leurs connexions durent longtemps.



