Python MQTT TLS, c'est la différence entre envoyer votre nom d'utilisateur, votre mot de passe et vos données de capteurs en clair sur Internet, et les envoyer chiffrés et authentifiés. Dans ce tutoriel, vous allez configurer paho-mqtt pour se connecter sur le port 8883 à l'aide des certificats CA de confiance de votre système d'exploitation, garder la vérification du nom d'hôte activée et corriger les erreurs de certificat qui bloquent la plupart des gens. Chaque exemple se connecte à broker.connectmq.com, qui présente un certificat Let's Encrypt reconnu publiquement : aucun fichier CA personnalisé n'est nécessaire.
Ce que TLS apporte à MQTT
MQTT n'intègre aucun chiffrement. Sur le port 1883, tout circule sous forme d'octets lisibles, y compris le paquet CONNECT contenant vos identifiants. TLS encapsule la connexion TCP et vous offre trois garanties :
- Confidentialité : personne sur le chemin réseau ne peut lire les payloads ni les mots de passe.
- Intégrité : personne ne peut modifier les messages en transit sans que cela soit détecté.
- Authentification du serveur : votre client vérifie qu'il parle bien au broker demandé, et non à un imposteur.
C'est ce troisième point que l'on désactive par mégarde. Le chiffrement sans vérification vous protège des écoutes passives, mais pas d'un attaquant actif qui présente son propre certificat. Le guide des bonnes pratiques de sécurité MQTT couvre la vue d'ensemble ; cet article se concentre sur la bonne configuration côté client.
ConnectMQ expose ces ports sur chaque broker :
| Port | Transport | Chiffré | Usage depuis Python |
|---|---|---|---|
| 1883 | MQTT sur TCP | Non | Tests locaux uniquement |
| 8883 | MQTT sur TLS | Oui | Choix par défaut en production |
| 8083 | MQTT sur WebSocket | Non | Rarement utile depuis Python |
| 8084 | MQTT sur WebSocket sécurisé, chemin /mqtt |
Oui | Quand seuls les ports web sont autorisés |
Prérequis
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install "paho-mqtt>=2.0"
Vérifiez que le certificat du broker est validé depuis votre machine avant d'écrire la moindre ligne de Python. Cette commande affiche la chaîne et le résultat de la vérification :
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -brief </dev/null
Cherchez Verification: OK. Si OpenSSL échoue déjà à ce stade, le problème vient du magasin de CA de votre système ou du réseau, pas de votre code.
Le client Python MQTT TLS minimal
Enregistrez ceci sous tls_client.py et remplacez k7f3q2 par votre propre chaîne aléatoire :
import ssl
import time
import uuid
import paho.mqtt.client as mqtt
BROKER = "broker.connectmq.com"
PORT = 8883
TOPIC = "connectmq/demo/k7f3q2/secure"
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"Connect failed: {reason_code}")
return
sock = client.socket()
print(f"Connected over {sock.version()} using {sock.cipher()[0]}")
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=f"tls-{uuid.uuid4().hex[:8]}")
client.on_connect = on_connect
client.on_message = on_message
# Utiliser les CA de confiance du système d'exploitation, exiger un certificat valide
# et vérifier qu'il correspond à BROKER. C'est le comportement sécurisé par défaut.
client.tls_set(cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLS_CLIENT)
client.connect(BROKER, PORT, keepalive=60)
client.loop_start()
time.sleep(2)
client.publish(TOPIC, "hello over TLS", qos=1)
time.sleep(2)
client.disconnect()
client.loop_stop()
Lancez-le : vous devriez voir quelque chose comme Connected over TLSv1.3 using TLS_AES_256_GCM_SHA384, suivi du retour de votre message.
Ce que fait tls_set()
Appelé sans argument ca_certs, tls_set() construit un contexte SSL qui charge les certificats de confiance par défaut de la plateforme, exige que le serveur présente un certificat valide et vérifie que le nom du certificat correspond à l'hôte passé à connect(). Les arguments explicites cert_reqs et tls_version ci-dessus correspondent aux valeurs par défaut ; ils sont écrits pour que l'intention soit évidente pour le prochain lecteur.
Si vous préférez garder un contrôle total, construisez le contexte vous-même et transmettez-le :
context = ssl.create_default_context()
context.minimum_version = ssl.TLSVersion.TLSv1_2
client.tls_set_context(context)
ssl.create_default_context() active la vérification du nom d'hôte et du certificat, et la définition d'une version minimale rejette les protocoles obsolètes. Utilisez soit tls_set(), soit tls_set_context(), mais pas les deux.
Ajouter des identifiants pour un broker privé
TLS protège les identifiants que vous envoyez : combinez donc toujours les deux. Avec un broker créé dans le portail ConnectMQ :
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="YOUR_CLIENT_ID")
client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.tls_set()
client.connect("broker.connectmq.com", 8883, keepalive=60)
Utilisez le nom d'hôte et le client ID affichés dans le portail ; le broker associe les client IDs aux identifiants qu'il délivre. Chargez les secrets depuis des variables d'environnement (os.environ["MQTT_PASSWORD"]) plutôt que de les committer dans votre gestionnaire de sources.
WebSocket sécurisé depuis Python
Certains réseaux bloquent tout sauf les ports web standard. paho peut faire transiter MQTT par un WebSocket sécurisé sur le port 8084, avec la même vérification TLS :
client = mqtt.Client(
mqtt.CallbackAPIVersion.VERSION2,
client_id=f"wss-{uuid.uuid4().hex[:8]}",
transport="websockets",
)
client.ws_set_options(path="/mqtt")
client.tls_set()
client.connect("broker.connectmq.com", 8084, keepalive=60)
Tout le reste, callbacks, abonnements et publication, reste identique. L'article MQTT sur WebSocket explique quand ce transport est pertinent.
Corriger les erreurs TLS courantes
La plupart des échecs TLS se résument à une poignée de causes. Faites correspondre votre message d'erreur à ce tableau :
| Erreur | Cause probable | Correction |
|---|---|---|
CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate |
Python ne trouve pas les CA de confiance | Voir les corrections du magasin de CA ci-dessous |
Hostname mismatch, certificate is not valid for '...' |
Connexion par adresse IP ou par un alias | Se connecter avec le nom d'hôte exact du broker |
certificate is not yet valid ou certificate has expired |
L'horloge de l'appareil est fausse | Synchroniser l'heure par NTP avant la connexion |
WRONG_VERSION_NUMBER |
Client TLS qui parle au port en clair 1883 | Utiliser le port 8883 |
| La connexion se fige puis expire | Client en clair qui parle au port TLS 8883, ou port bloqué | Appeler tls_set(), ou essayer le port 8084 |
Corriger un magasin de CA manquant
- macOS avec Python de python.org : cette version de Python n'a pas accès au trousseau système. Exécutez le script « Install Certificates.command » du dossier de l'application Python, ou pointez paho vers certifi :
pip install certifi, puisclient.tls_set(ca_certs=certifi.where()). - Images Docker allégées : installez le bundle de CA, par exemple
apt-get install -y ca-certificatessur les images basées sur Debian. - Réseaux d'entreprise avec inspection TLS : un proxy peut remplacer les certificats par les siens. Demandez au service informatique la CA racine de l'entreprise et passez-la avec
ca_certs=plutôt que de désactiver la vérification. - Linux embarqué : mettez à jour le bundle de CA ; les images très anciennes peuvent ne pas inclure le certificat ISRG Root X1 auquel se rattache Let's Encrypt.
Ne jamais « corriger » TLS en désactivant la vérification
Vous trouverez des conseils recommandant d'appeler client.tls_insecure_set(True) ou de passer cert_reqs=ssl.CERT_NONE. Les deux font disparaître l'erreur en acceptant n'importe quel certificat de n'importe qui, ce qui supprime silencieusement l'authentification du serveur. Ne les utilisez que pour un diagnostic rapide sur votre propre réseau de test, jamais dans du code déployé.
Évitez aussi d'épingler (pinning) le certificat feuille du broker dans votre client. Les certificats Let's Encrypt sont renouvelés régulièrement, donc un certificat épinglé cassera au prochain renouvellement. Faites plutôt confiance à la CA, ce que fait le magasin par défaut.
Checklist de production
Avant de déployer un client Python utilisant TLS, vérifiez chaque point :
- Le port 8883 (ou 8084 avec WebSocket) est utilisé partout ; rien ne se rabat sur 1883.
tls_set()outls_set_context()est appelé sanstls_insecure_set(True).- Le client se connecte par nom d'hôte, jamais par adresse IP.
- L'appareil garde une heure exacte, synchronisée par NTP au démarrage.
- Le bundle de CA de l'appareil est maintenu et mis à jour avec le système.
- Les identifiants proviennent de variables d'environnement ou d'un coffre de secrets.
- Une logique de reconnexion est en place ; le guide de reconnexion paho-mqtt présente un modèle robuste.
Pour aller plus loin
Si vous débutez avec paho, le tutoriel paho mqtt python couvre les bases du publish et du subscribe, et le guide aiomqtt et asyncio montre comment passer un tls_context à un client asynchrone. Pour les clients dans d'autres langages, le panorama des bibliothèques clientes MQTT compare la prise en charge de TLS selon les écosystèmes. La documentation du module ssl de Python est la référence pour chaque option de contexte utilisée ici.
Sécurisez votre trafic MQTT avec ConnectMQ
ConnectMQ est un broker MQTT managé avec TLS sur le port 8883 et WebSocket sécurisé sur le port 8084 activés d'emblée, adossés à un certificat Let's Encrypt géré automatiquement : vos clients Python vérifient le broker avec le magasin de CA dont ils disposent déjà. Testez sur le serveur MQTT public gratuit (réservé aux tests, pas aux données de production), puis passez à un broker MQTT cloud privé avec des identifiants générés. Créez votre broker gratuit sur le plan Serverless, sans carte bancaire.
Questions fréquentes
Dois-je télécharger un certificat CA pour me connecter avec TLS ?
Non. Le broker utilise un certificat Let's Encrypt rattaché à une racine reconnue par les systèmes d'exploitation actuels, donc tls_set() avec le magasin de CA par défaut fonctionne.
Quel port utilise MQTT sur TLS ?
Le port standard est 8883. ConnectMQ propose aussi MQTT sur WebSocket sécurisé sur le port 8084 avec le chemin /mqtt.
Pourquoi Python MQTT TLS échoue-t-il quand je me connecte par adresse IP ?
La vérification du nom d'hôte compare le nom du certificat à l'hôte auquel vous vous êtes connecté. Une adresse IP ne correspond pas au nom du broker, donc la vérification échoue par conception ; utilisez le nom d'hôte.
TLS suffit-il à sécuriser mon client MQTT ?
TLS protège la connexion, mais il vous faut toujours des identifiants robustes et propres à chaque appareil, ainsi qu'une conception soignée des topics. Combinez TLS avec des identifiants uniques issus du portail du broker.



