Sécuriser les identifiants MQTT sur les appareils, c'est s'assurer que le nom d'utilisateur et le mot de passe utilisés pour joindre le broker ne peuvent pas être lus depuis les images firmware, des puces flash extraites, les ports de débogage ou les fichiers journaux. Ce guide compare les options concrètes sur les appareils ESP32 et Linux, présente la configuration de chacune et liste les erreurs qui font le plus souvent fuiter des identifiants sur le terrain.
Le modèle de menace en termes simples
Avant de choisir une méthode de stockage, sachez clairement contre qui vous vous défendez :
| Attaquant | Accès | Ce qu'il tente |
|---|---|---|
| Quelqu'un qui a votre fichier firmware | Un .bin issu d'une page de téléchargement ou d'un ticket de support |
Rechercher des chaînes dans l'image |
| Quelqu'un qui a un appareil | Accès physique, un adaptateur USB-série | Extraire la flash via UART, se brancher en JTAG |
| Un malware ou un bug sur l'appareil | Du code exécuté sur l'appareil | Lire des fichiers ou la mémoire |
| Quelqu'un sur le réseau | Capture de paquets | Lire le mot de passe dans le CONNECT |
L'attaquant réseau est neutralisé par TLS sur le port 8883 (ou 8084 pour les WebSockets), que chaque broker ConnectMQ prend en charge avec un certificat Let's Encrypt ; l'authentification MQTT par nom d'utilisateur et mot de passe explique pourquoi les identifiants ne doivent jamais circuler sur le port 1883. Cet article se concentre sur les trois autres : la protection du secret au repos sur l'appareil.
Règle zéro : les identifiants n'ont rien à faire dans le firmware
Un #define MQTT_PASSWORD "..." dans le code source se retrouve dans le binaire du firmware, dans votre historique Git, dans les journaux de CI et dans chaque copie de l'image. Lancer strings firmware.bin | grep -i pass sur un projet amateur typique le trouve souvent en quelques secondes. Le firmware doit être identique pour tous les appareils ; les identifiants doivent être uniques par appareil. Séparez-les et écrivez les identifiants dans le stockage lors du provisionnement sécurisé des appareils IoT.
Options sur l'ESP32
NVS en clair (niveau de base, non sécurisé)
La bibliothèque Non-Volatile Storage (ou son enveloppe Arduino Preferences) stocke des paires clé-valeur dans une partition de la flash. Elle sépare les identifiants du firmware, ce qui constitue déjà un progrès, mais les données sont stockées sans chiffrement. Quiconque peut lire la puce flash peut lire le mot de passe.
Chiffrement NVS et chiffrement de la flash
ESP-IDF peut chiffrer les partitions NVS. Dans le schéma classique, les clés de chiffrement NVS se trouvent dans une partition nvs_keys dédiée, elle-même protégée par le chiffrement de la flash, dont la clé est stockée dans des eFuses que le logiciel ne peut pas relire. Les puces plus récentes comme l'ESP32-S3 et l'ESP32-C3 prennent aussi en charge un schéma basé sur HMAC qui dérive les clés NVS d'une clé eFuse, sans partition nvs_keys.
Une table de partitions avec une partition de clés chiffrée :
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, , 0x6000,
nvs_keys, data, nvs_keys, , 0x1000, encrypted
phy_init, data, phy, , 0x1000,
factory, app, factory, , 1M,
Les options sdkconfig pertinentes :
CONFIG_SECURE_FLASH_ENC_ENABLED=y
CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE=y
CONFIG_NVS_ENCRYPTION=y
CONFIG_SECURE_BOOT=y
CONFIG_SECURE_BOOT_V2_ENABLED=y
Le mode release est important : le mode développement laisse des moyens de reflasher des images en clair, ce qui annule l'intérêt du dispositif sur les unités livrées. Secure Boot complète le chiffrement en refusant d'exécuter un firmware que vous n'avez pas signé, si bien qu'un attaquant ne peut pas flasher du code qui lit et affiche les identifiants déchiffrés. Les deux grillent des eFuses de façon irréversible ; testez donc d'abord sur des cartes de rechange et suivez pas à pas la documentation Espressif sur le chiffrement de la flash.
La lecture des identifiants dans un sketch Arduino sur ESP32 est identique que la NVS soit chiffrée ou non, et c'est précisément le but : la protection se situe sous l'API.
#include <Preferences.h>
struct MqttCreds { String user, pass, clientId; };
bool loadCreds(MqttCreds &c) {
Preferences prefs;
if (!prefs.begin("mqtt", true)) return false; // lecture seule
c.user = prefs.getString("user", "");
c.pass = prefs.getString("pass", "");
c.clientId = prefs.getString("cid", "");
prefs.end();
return c.user.length() && c.pass.length() && c.clientId.length();
}
void logCreds(const MqttCreds &c) {
// Journaliser l'identité, jamais le mot de passe
Serial.printf("MQTT user=%s clientId=%s pass=<%u bytes>\n",
c.user.c_str(), c.clientId.c_str(), c.pass.length());
}
Éléments sécurisés
Un élément sécurisé comme l'ATECC608 de Microchip est une puce distincte qui stocke des clés et effectue des opérations cryptographiques sans jamais divulguer les clés privées. Il est le plus efficace pour une identité basée sur des certificats, où la clé privée du TLS mutuel ne quitte jamais la puce. Avec une authentification par mot de passe, un élément sécurisé peut tout de même aider en détenant la clé qui chiffre le blob du mot de passe en flash, ce qui rend l'extraction physique plus difficile. Il ajoute un coût de nomenclature, de l'espace sur la carte et une intégration de pilote ; on le rencontre donc surtout dans les produits manipulant des données à forte valeur ou soumis à une pression réglementaire.
Options sur les appareils Linux
Les passerelles et ordinateurs edge sous Linux disposent d'outils plus riches.
Permissions de fichiers (niveau de base)
Stockez les identifiants dans un fichier lisible uniquement par le compte de service, en dehors du répertoire de l'application :
sudo install -d -m 0700 -o mqttsvc -g mqttsvc /etc/mqtt-agent
sudo install -m 0600 -o mqttsvc -g mqttsvc credentials.env /etc/mqtt-agent/credentials.env
Cela protège contre les autres utilisateurs et services, pas contre quelqu'un qui retire la carte SD. Combinez-le avec un chiffrement complet du disque pour la protection physique.
Credentials chiffrés systemd avec un TPM
Sur les systèmes équipés de systemd et d'une puce TPM 2.0, systemd-creds chiffre un secret de sorte qu'il ne puisse être déchiffré que sur cette machine, et uniquement pour le service qui le déclare :
# Chiffrer le mot de passe, lié au TPM de cette machine
echo -n 'YOUR_PASSWORD' | sudo systemd-creds encrypt --with-key=tpm2 \
--name=mqtt-password - /etc/credstore.encrypted/mqtt-password
# /etc/systemd/system/mqtt-agent.service
[Unit]
Description=MQTT telemetry agent
After=network-online.target
Wants=network-online.target
[Service]
User=mqttsvc
LoadCredentialEncrypted=mqtt-password:/etc/credstore.encrypted/mqtt-password
Environment=MQTT_USERNAME=YOUR_USERNAME MQTT_CLIENT_ID=YOUR_CLIENT_ID
ExecStart=/usr/bin/python3 /opt/mqtt-agent/agent.py
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
Le service lit le secret déchiffré depuis un répertoire privé en mémoire :
import os
import ssl
import paho.mqtt.client as mqtt
cred_dir = os.environ["CREDENTIALS_DIRECTORY"]
with open(os.path.join(cred_dir, "mqtt-password")) as f:
password = f.read().strip()
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id=os.environ["MQTT_CLIENT_ID"],
protocol=mqtt.MQTTv5)
client.username_pw_set(os.environ["MQTT_USERNAME"], password)
client.tls_set(cert_reqs=ssl.CERT_REQUIRED)
client.connect("YOUR_BROKER_HOST", 8883, keepalive=60)
client.loop_forever()
Une image disque copiée est inutilisable sur une autre machine, car le TPM refusera de desceller l'identifiant.
Comparaison des options de stockage
| Méthode | Plateforme | Protège contre les fuites de firmware | Protège contre l'extraction de la flash | Effort |
|---|---|---|---|---|
| Codé en dur dans le firmware | Toutes | Non | Non | Aucun |
| NVS en clair / fichier de configuration | ESP32 / Linux | Oui | Non | Faible |
| Chiffrement NVS et de la flash, Secure Boot | ESP32 | Oui | Oui | Moyen, eFuses irréversibles |
| Élément sécurisé | ESP32, Linux | Oui | Oui, le plus solide pour les clés | Élevé |
| Permissions de fichiers et chiffrement du disque | Linux | Oui | Partiellement | Faible à moyen |
| systemd-creds avec TPM | Linux | Oui | Oui | Moyen |
Identifiants en mémoire, sauvegardes et mises à jour
Le stockage chiffré protège l'identifiant au repos, mais il doit tout de même être déchiffré en RAM pour construire le paquet CONNECT. Quelques habitudes réduisent l'exposition sur le reste de son parcours :
- Le garder à un seul endroit. Chargez l'identifiant une seule fois dans un tampon unique appartenant au module MQTT, au lieu de le copier dans des chaînes globales, des structures de configuration et des objets de diagnostic.
- L'exclure des diagnostics. Les core dumps, rapports de plantage et envois de journaux à distance ne doivent jamais contenir le stockage des identifiants. Sous Linux, tenez le fichier d'identifiants à l'écart des jeux de sauvegarde et des bundles de support.
- Ne pas le faire transiter par le cloud. Une fonction de synchronisation de configuration qui envoie les réglages de l'appareil à votre backend doit ignorer totalement les champs d'identifiants.
- Écrire les mises à jour de façon atomique. Lorsqu'un nouvel identifiant arrive pendant une rotation, écrivez-le dans un second emplacement et ne basculez qu'après une connexion réussie, pour qu'une coupure de courant ne puisse pas laisser l'appareil avec un mot de passe à moitié écrit.
- L'effacer lors de la mise hors service. Effacez le namespace NVS ou le fichier d'identifiants dans le cadre des procédures de réinitialisation d'usine et de mise hors service.
Les erreurs qui font fuiter les identifiants
- Afficher le mot de passe dans les journaux de débogage ou les crash dumps.
- L'exposer sur une page web de l'appareil « par commodité », même masqué en HTML.
- Le stocker dans un retained message MQTT ou l'envoyer sur un topic partagé.
- Laisser le mode de téléchargement UART et le JTAG activés sur les unités de production.
- Utiliser un seul identifiant pour tout le parc, de sorte qu'une seule extraction compromet tout.
- Ne jamais faire de rotation, de sorte qu'un identifiant divulgué il y a des années fonctionne encore.
Le point 5 mérite d'être souligné. Sur ConnectMQ, chaque client ID est lié aux identifiants qui lui ont été émis ; donnez donc à chaque appareil son propre jeu. Un mot de passe extrait n'expose alors qu'un appareil, que vous pouvez révoquer et remplacer grâce au processus décrit dans comment faire la rotation des identifiants MQTT. L'isolation des tenants signifie que même les identifiants d'un appareil compromis ne donnent accès qu'à votre propre broker, et le blocage d'IP après des échecs de connexion répétés ralentit les attaquants qui tentent de deviner d'autres mots de passe. L'ensemble plus large des contrôles est traité dans les bonnes pratiques de sécurité MQTT.
Démarrer avec ConnectMQ
Associez un stockage protégé sur l'appareil à un broker MQTT managé qui gère pour vous TLS et l'émission des identifiants. Créez gratuitement un broker Serverless sur app.connectmq.com, sans carte bancaire, et testez le stockage des identifiants de votre ESP32 ou de votre appareil Linux face à un véritable endpoint TLS. Le serveur MQTT public gratuit broker.connectmq.com suffit pour un tout premier essai de connexion anonyme, mais il est réservé aux tests et aux prototypes : n'y utilisez jamais de vrais identifiants ni de données de production. Si vous débutez côté matériel, le tutoriel MQTT ESP32 est un bon point de départ, et le guide du broker MQTT cloud présente les options d'hébergement.
Questions fréquentes
La bibliothèque Preferences de l'ESP32 est-elle sûre pour les mots de passe MQTT ?
Seulement si le chiffrement NVS et le chiffrement de la flash sont activés. Sans eux, Preferences stocke les données en clair dans la flash, que n'importe qui disposant d'un accès physique peut extraire.
Faut-il un élément sécurisé pour les identifiants MQTT ?
Pas toujours. Un stockage chiffré avec chiffrement de la flash et Secure Boot suffit pour de nombreux produits ; un élément sécurisé apporte surtout de la valeur pour une identité basée sur des certificats ou des déploiements à forte valeur.
Où une passerelle Linux doit-elle stocker son mot de passe MQTT ?
Au minimum dans un fichier en 0600 appartenant au compte de service. Lorsqu'un TPM est disponible, les credentials chiffrés systemd lient le secret à cette machine.
TLS protège-t-il les identifiants stockés sur l'appareil ?
Non. TLS protège les identifiants en transit. La protection du stockage est une couche distincte, nécessaire sur l'appareil lui-même.



