1 nouveauté hors suivi
Leçon

La surface d'attaque d'un système IoT

Jour 1 · Attaquer pour comprendre · 25 min

Cadre de ce module

Les manipulations offensives de M5 portent exclusivement sur votre propre maquette — le broker, les capteurs et la plateforme que vous avez construits depuis M1. Rejouer ces techniques sur un système que vous n'exploitez pas est illégal, quelle que soit l'intention.

Cette règle n'est pas une formalité : elle fait partie de ce qui est évalué à la revue de module.

Un système IoT présente plus de surfaces qu'une application classique, parce qu'il déborde du réseau : il a un corps physique, une radio, et il se met à jour tout seul.

Objet accès physique firmware extractible Radio écoute rejeu, brouillage Broker injection déni de service Plateforme jetons exposés API ouverte Mise à jour du firmware — traverse toute la chaîne la plus grande porte d'entrée, et la seule qui touche tous les objets à la fois
Quatre surfaces, plus celle qui les traverse toutes. Chacune appelle des parades différentes.

L'OWASP IoT Top 10, appliqué à Thermeo

L'OWASP publie une liste des dix faiblesses les plus répandues. Confrontons-la à ce que vous avez construit — l'exercice est instructif.

Faiblesse OWASPVotre maquetteTraité en
Mots de passe faibles ou par défautcorrigé — comptes nominatifsM1
Services réseau non sécuriséscorrigé — TLS, port 1883 ferméM1
Interfaces d'écosystème non sûrespartiel — l'API de M4 n'a pas d'authentificationM4
Absence de mise à jour sûrenon traité — c'est l'objet du jour 2M5
Composants obsolètesà vérifier — vos images Docker sont-elles à jour ?M5
Protection insuffisante de la vie privéeà examiner — que révèlent vos relevés ?M5
Transfert et stockage non chiffréspartiel — MQTT chiffré, InfluxDB en clairM5
Absence de gestion du parcnon traité — pas d'inventaire des certificatsM5
Réglages par défaut non sûrsà vérifier — mot de passe Grafana, jetonsM5
Absence de durcissement physiquehors périmètre en simulé, mais à documenterM5
Ce que ce tableau montre

Vous avez traité les deux premières faiblesses dès M1, ce qui est déjà mieux que beaucoup de déploiements réels. Les six suivantes restent ouvertes — c'est le travail du module.

Ce que l'IoT a de particulier

  • L'objet est physiquement accessible. Un capteur sur un toit peut être emporté, ouvert, son firmware extrait et lu. Tout secret qu'il contient est un secret public.
  • Le parc est nombreux et durable. Cent objets déployés pour dix ans : une faille découverte en 2029 devra être corrigée sur du matériel installé en 2026.
  • La mise à jour est une arme. Le mécanisme qui vous permet de corriger une faille permet aussi, s'il est mal protégé, d'installer n'importe quoi sur tout le parc.
Le secret embarqué

Le firmware que vous écrivez sera lisible par quiconque tient l'objet. Un mot de passe en dur, une clé d'API, un jeton : considérez-les comme publics dès l'instant où vous les compilez.

C'est pourquoi le certificat client de M1 est supérieur au mot de passe : il est propre à chaque objet et se révoque individuellement.

✓ terminé
Quiz

Quiz — Surface d'attaque

Jour 1 · Attaquer pour comprendre · 7 min

1. Pourquoi un mot de passe en dur dans le firmware est-il un problème ?

2. En quoi le certificat client de M1 est-il supérieur au mot de passe ?

3. Quelle surface d'attaque touche potentiellement tout le parc d'un coup ?

4. Votre InfluxDB stocke les relevés en clair sur le disque. Est-ce grave ?

✓ terminé
Travaux pratiques

TP 1 — Auditer sa propre maquette

Jour 1 · Attaquer pour comprendre · 1 h 15

Passer votre installation au crible des dix faiblesses OWASP, avec des preuves.

Méthode

Un audit n'est pas une opinion : chaque constat s'appuie sur une commande exécutée et son résultat. Consignez les deux.

  1. Inventoriez ce qui écoute sur votre poste. Toute écoute non justifiée est un constat :
    docker ps --format 'table {{{{.Names}}}}\t{{{{.Ports}}}}'
    lsof -iTCP -sTCP:LISTEN -P -n | grep -E '1883|8883|8086|3000|1880|8000'
  2. Pour chaque port, répondez par écrit : qui doit y accéder, et qu'est-ce qui l'en empêche aujourd'hui ?
  3. Vérifiez les réglages par défaut. Grafana, InfluxDB, Node-RED sont-ils protégés ?
    curl -s -o /dev/null -w 'grafana  %{{http_code}}\n' http://localhost:3000/api/health
    curl -s -o /dev/null -w 'influx   %{{http_code}}\n' http://localhost:8086/api/v2/buckets
    curl -s -o /dev/null -w 'nodered  %{{http_code}}\n' http://localhost:1880/flows
  4. Node-RED répond très probablement sans authentification, et il peut publier sur votre broker. Mesurez ce que cela permet à quelqu'un sur le même réseau.
  5. Testez votre API de M4 : est-elle authentifiée ?
    curl -s http://localhost:8000/api/etat | head -c 200
  6. Cherchez les secrets qui traînent dans votre dépôt — la faiblesse la plus répandue :
    git log --all -p | grep -iE 'token|password|BEGIN (RSA|PRIVATE)' | head -20
    git ls-files | xargs grep -lIE 'token|passwd|\.key$' 2>/dev/null
  7. Examinez la question de la vie privée : à partir de vos relevés de température de bureau, que peut-on déduire ? Écrivez la réponse — horaires d'occupation, absences, week-ends. C'est le constat que les équipes oublient systématiquement.
  8. Vérifiez l'âge de vos images :
    docker images --format '{{{{.Repository}}}}:{{{{.Tag}}}}\t{{{{.CreatedSince}}}}'
  9. Produisez un tableau d'audit : une ligne par faiblesse OWASP, avec constat, preuve, gravité et correction proposée. C'est le premier livrable du module.
Questions de réflexion — à préparer par écrit
  • Combien de vos constats viennent d'un oubli, et combien d'un choix assumé pour la maquette ?
  • Lequel corrigeriez-vous en premier si la maquette partait en production demain ?
✓ terminé
Leçon

Écouter, rejouer, injecter

Jour 1 · Attaquer pour comprendre · 20 min

Trois attaques suffisent à comprendre pourquoi le chiffrement seul ne protège pas.

L'écoute

Sans TLS, tout transite en clair : valeurs, identifiants, topics. Vous l'avez déjà constaté au TP 3 de M1 avec Wireshark. C'est l'attaque la plus simple, et la parade est connue.

Le rejeu

Plus subtile, parce qu'elle survit au chiffrement dans certains montages. Le principe : capturer un message légitime et le réémettre plus tard.

Pourquoi le chiffrement n'y suffit pas toujours

Si un attaquant obtient un jour un identifiant valide, il peut republier indéfiniment un message correctement formé. Le broker n'a aucun moyen de distinguer « le capteur dit 21,4 °C » de « quelqu'un rejoue un ancien 21,4 °C ».

Sur une mesure, c'est gênant. Sur une commande — « ouvrir la vanne » — c'est grave.

Trois parades, à combiner :

  • Horodatage dans la charge utile, et rejet de tout message trop ancien. Impose des horloges synchronisées.
  • Compteur croissant par capteur : un numéro déjà vu est refusé.
  • Signature du message incluant l'horodatage, pour qu'on ne puisse pas le modifier.

L'injection

Publier de fausses valeurs sur un topic légitime. C'est le scénario évoqué en M1 : une température falsifiée qui trompe la régulation.

La parade est celle que vous avez déjà en place — les ACL par certificat. Encore faut-il qu'elles soient strictes : un capteur qui a le droit d'écrire sur thermeo/# peut usurper tous les autres.

Le lien avec la détection

Une valeur injectée est souvent aberrante — un saut brutal, une valeur physiquement impossible. Le filtrage médian de M2 et les seuils de plausibilité rendent l'injection beaucoup plus difficile à exploiter : c'est de la défense en profondeur.

✓ terminé
Travaux pratiques

TP 2 — Rejouer contre soi-même

Jour 1 · Attaquer pour comprendre · 1 h 15

Monter une attaque par rejeu sur votre maquette, puis la rendre inopérante.

Rappel

Uniquement sur votre installation. Aucune de ces manipulations ne doit viser un système que vous n'exploitez pas.

  1. Placez-vous dans la peau d'un attaquant qui a obtenu un identifiant. Capturez un message légitime :
    mosquitto_sub -h localhost -p 8883 --cafile certs/ca.crt \
      --cert certs/capt-01.crt --key certs/capt-01.key \
      -t 'thermeo/lyon/etage1/capt-01/temperature' -C 1 -v
  2. Rejouez-le une heure plus tard, tel quel :
    mosquitto_pub -h localhost -p 8883 --cafile certs/ca.crt \
      --cert certs/capt-01.crt --key certs/capt-01.key \
      -t 'thermeo/lyon/etage1/capt-01/temperature' -m '21.4'
  3. Regardez Grafana : la valeur rejouée est indiscernable d'une mesure réelle. Le système est chiffré, authentifié, et pourtant trompé.
  4. Ajoutez maintenant les défenses. Enrichissez la charge utile du capteur avec un horodatage et un compteur :
    import time, json
    
    compteur = 0
    
    def charge(valeur):
        global compteur
        compteur += 1
        return json.dumps({
            "valeur": valeur,
            "ts": int(time.time()),
            "n": compteur,
        })
  5. Écrivez côté réception un contrôle qui rejette les messages suspects :
    import time
    
    FENETRE = 60          # secondes de tolerance
    vus = {}              # capteur -> dernier compteur accepte
    
    def accepter(capteur, msg):
        if abs(time.time() - msg["ts"]) > FENETRE:
            return False, "horodatage hors fenetre"
        if msg["n"] <= vus.get(capteur, 0):
            return False, "compteur deja vu"
        vus[capteur] = msg["n"]
        return True, "ok"
  6. Rejouez le message capturé : il doit être rejeté deux fois — par l'horodatage et par le compteur. Consignez les deux refus.
  7. Éprouvez la limite : rejouez immédiatement, dans la fenêtre de 60 secondes. Seul le compteur vous sauve. Que se passe-t-il si le capteur redémarre et repart à zéro ? Traitez ce cas — il est réel, un capteur en deep sleep redémarre à chaque réveil.
  8. Ajoutez un contrôle de plausibilité : rejeter toute valeur hors de −30 à +60 °C, ou s'écartant de plus de 10 °C de la précédente. Testez avec une injection aberrante.
Questions de réflexion — à préparer par écrit
  • Le compteur ne survit pas au redémarrage d'un objet en deep sleep. Quelle solution ?
  • L'horodatage suppose des horloges synchronisées. Comment un capteur sur pile obtient-il l'heure ?
  • Vos trois parades — horodatage, compteur, plausibilité — sont-elles redondantes ou complémentaires ?
✓ terminé
Point de contrôle

Point de contrôle — fin du jour 1

Jour 1 · Attaquer pour comprendre · 15 min

Vérifiez avant de continuer
Mon tableau d'audit couvre les dix faiblesses OWASP, avec preuve pour chacune
J'ai relevé les services accessibles sans authentification sur ma maquette
J'ai cherché les secrets dans l'historique de mon dépôt
J'ai écrit ce que mes relevés permettent de déduire sur l'occupation des locaux
J'ai réussi une attaque par rejeu sur ma propre installation
Horodatage et compteur rejettent maintenant le message rejoué
J'ai traité le cas du compteur après redémarrage
Le contrôle de plausibilité rejette une valeur aberrante
✓ terminé
Leçon

Mettre à jour sans se tirer dessus

Jour 2 · Durcir et déployer · 25 min

Cent capteurs sur des toits. Une faille est publiée. Vous devez corriger — et le mécanisme qui vous le permet est précisément celui qui peut détruire le parc.

Avant v1 — active libre Écriture v1 — active v2 en cours Après bascule v1 — repli v2 — active Une coupure pendant l'écriture laisse v1 intacte — l'objet redémarre dessus Si v2 ne confirme pas son bon démarrage, l'objet repart sur v1
La double partition : on n'écrase jamais la version qui fonctionne.

Les trois exigences

1. La signature

L'objet doit vérifier que la mise à jour vient de vous. Le principe est celui des certificats de M1, appliqué à un fichier : vous signez le firmware avec une clé privée, l'objet vérifie avec la clé publique qu'il embarque.

Sans signature

Quiconque atteint l'objet — même réseau, DNS détourné, serveur de mise à jour compromis — installe son propre code. Et ce code s'exécute avec tous les privilèges, sur un objet qui a un certificat valide pour votre broker.

2. La double partition

On n'écrase jamais la version qui fonctionne. La nouvelle s'écrit dans la partition libre, et la bascule n'a lieu qu'une fois l'écriture vérifiée. Une coupure en cours de transfert laisse l'ancienne version intacte.

3. Le retour arrière

La nouvelle version doit confirmer son bon démarrage — se connecter au broker, publier une fois. Sans confirmation dans un délai donné, l'objet repart sur l'ancienne partition.

Le détail qui sauve un parc

Sans confirmation explicite, un firmware qui démarre mais ne parvient plus à se connecter est un objet perdu : il tourne, donc il ne redémarre pas, mais il est injoignable. Il faut aller le chercher physiquement.

La confirmation transforme une panne logicielle en simple retour arrière.

Le déploiement par vagues

Même signé, même avec retour arrière, on ne déploie jamais sur cent objets d'un coup.

VagueObjetsAttenteCe qu'on vérifie
11 objet, accessible facilement24 hQu'il fonctionne vraiment
210 objets, sites variés48 hComportement sur configurations différentes
3le resteSurveillance renforcée
L'objet témoin

Gardez un objet identique au parc, accessible physiquement, sur lequel vous déployez toujours en premier. C'est votre canari : s'il tombe, vous le récupérez à la main plutôt que d'aller chercher cent capteurs.

Ce que le simulateur ne fera pas

Limite assumée

Wokwi n'expose pas de serveur de mise à jour : nous ne réaliserons pas d'OTA par le réseau. En revanche, vous implémenterez pour de vrai la signature et sa vérification — c'est la partie qui compte, et celle qu'on rate.

La double partition et le retour arrière sont des mécanismes de l'ESP-IDF que vous documenterez sans les exécuter.

✓ terminé
Travaux pratiques

TP 3 — Signer et vérifier un firmware

Jour 2 · Durcir et déployer · 1 h

Implémenter la chaîne de confiance d'une mise à jour, de la signature au refus.

  1. Générez la paire de clés de signature. Elle est distincte de l'autorité TLS de M1 : signer du code et authentifier un service sont deux usages différents.
    openssl genrsa -out certs/firmware-signature.key 2048
    openssl rsa -in certs/firmware-signature.key -pubout \
      -out certs/firmware-signature.pub
  2. Fabriquez un firmware factice et signez-le :
    echo 'FIRMWARE v2 — corrige la faille du parseur' > firmware-v2.bin
    
    openssl dgst -sha256 -sign certs/firmware-signature.key \
      -out firmware-v2.sig firmware-v2.bin
  3. Écrivez le vérificateur — c'est ce que ferait l'objet avant d'installer :
    openssl dgst -sha256 -verify certs/firmware-signature.pub \
      -signature firmware-v2.sig firmware-v2.bin
  4. La commande répond Verified OK. Maintenant, attaquez : modifiez un octet du firmware et relancez la vérification.
  5. Le contrôle échoue. C'est exactement ce qui doit se produire :
    echo 'code malveillant' >> firmware-v2.bin
    openssl dgst -sha256 -verify certs/firmware-signature.pub \
      -signature firmware-v2.sig firmware-v2.bin
    # Verification Failure
  6. Écrivez maintenant un script maj.py qui reproduit la logique complète d'un objet recevant une mise à jour :
    # 1. telecharger firmware + signature
    # 2. verifier la signature -> si echec, ABANDON, on ne touche a rien
    # 3. verifier que la version est superieure a l'actuelle
    # 4. ecrire dans la partition inactive
    # 5. marquer 'a essayer' et redemarrer
    # 6. au demarrage : si pas de confirmation en 60 s -> retour arriere
    #
    # Simulez les partitions par deux fichiers et l'etat par un troisieme.
  7. Éprouvez les quatre scénarios et consignez le comportement de chacun : signature valide, signature invalide, version inférieure ou égale, et démarrage sans confirmation.
  8. Le cas de la version inférieure est le plus souvent oublié : sans lui, un attaquant réinstalle une ancienne version signée — qui contenait la faille que vous venez de corriger. C'est une attaque par retour en arrière.
Questions de réflexion — à préparer par écrit
  • Où stockeriez-vous la clé privée de signature, et qui doit y avoir accès ?
  • Pourquoi séparer la clé de signature de firmware de l'autorité TLS de M1 ?
  • Comment un objet sait-il quelle est sa version actuelle après un retour arrière ?
✓ terminé
Leçon

L'industriel n'est pas l'informatique

Jour 2 · Durcir et déployer · 20 min

Thermeo intervient dans des bâtiments tertiaires. Le jour où le client est une usine, les règles changent — et l'erreur classique est d'y appliquer les réflexes de l'informatique de gestion.

Deux mondes, deux priorités

Informatique de gestion (IT)Industriel (OT)
PrioritéConfidentialité → Intégrité → DisponibilitéDisponibilité → Intégrité → Confidentialité
Durée de vie3 à 5 ans15 à 30 ans
Mise à jourautomatique, fréquenteplanifiée, à l'arrêt de production
Redémarreracceptablearrête la ligne — coûte des milliers d'euros
ProtocolesHTTP, TLSModbus, PROFINET, OPC-UA
L'inversion qui surprend

En OT, la disponibilité prime sur la confidentialité. Un correctif de sécurité qui impose un redémarrage peut être refusé, parce qu'arrêter la production coûte plus cher que le risque estimé.

Ce n'est pas de la négligence : c'est un arbitrage économique explicite, et il faut savoir composer avec.

Les protocoles que vous rencontrerez

ProtocoleNatureSécurité native
ModbusTrès ancien, très répandu, très simpleaucune — ni authentification ni chiffrement
PROFINETEthernet industriel temps réellimitée, dépend de l'installation
OPC-UAModerne, orienté services et modèle de donnéesintégrée — authentification, chiffrement, signature
Modbus en une phrase

Quiconque atteint le réseau peut lire et écrire n'importe quel registre, sans s'identifier. Le protocole date de 1979 et n'a jamais prévu d'attaquant.

La parade n'est pas dans le protocole : elle est dans la segmentation réseau — on isole, on filtre, on ne l'expose jamais.

La segmentation, principe central de l'OT

Puisqu'on ne peut pas sécuriser les équipements eux-mêmes — trop anciens, trop critiques pour être touchés — on sécurise ce qui les entoure. Le modèle de référence empile des niveaux, du terrain à la bureautique, avec une zone démilitarisée entre l'industriel et l'informatique de gestion.

Votre passerelle IoT se place précisément là : elle lit les équipements industriels et n'expose que ce qui doit sortir. Elle ne doit jamais permettre d'écrire dans le sens inverse sans contrôle explicite.

Le cas de la santé connectée

Le programme mentionne les réseaux corporels. La logique y est la même — disponibilité d'abord, un équipement médical ne se redémarre pas pendant un soin — avec en plus un régime réglementaire strict sur les données de santé. La segmentation et le principe du moindre privilège s'appliquent à l'identique.

✓ terminé
Quiz

Quiz — Mise à jour et industriel

Jour 2 · Durcir et déployer · 7 min

1. Pourquoi refuser une mise à jour dont la version est inférieure à l'actuelle ?

2. À quoi sert la confirmation de bon démarrage ?

3. En milieu industriel, quelle propriété prime généralement ?

4. Comment sécuriser un équipement Modbus ?

✓ terminé
Travaux pratiques

TP 4 — Le plan de durcissement

Jour 2 · Durcir et déployer · 1 h 30

Corriger ce que l'audit a révélé et produire le dossier de sécurité du système.

  1. Reprenez votre tableau d'audit du TP 1 et classez les constats par gravité, en croisant impact et facilité d'exploitation.
  2. Corrigez les trois plus graves. Typiquement : authentifier Node-RED, protéger l'API de M4, retirer les secrets du dépôt.
  3. Pour l'API, ajoutez une authentification par en-tête :
    from fastapi import Header, HTTPException
    import os, secrets
    
    def verifier(x_api_key: str = Header(None)):
        attendu = os.environ["API_KEY"]
        if not x_api_key or not secrets.compare_digest(x_api_key, attendu):
            raise HTTPException(401, "cle invalide")
    
    @app.get("/api/etat", dependencies=[Depends(verifier)])
    def etat():
        ...
  4. Notez l'usage de compare_digest plutôt que == : la comparaison à temps constant évite de révéler la clé caractère par caractère. Expliquez pourquoi dans votre rendu.
  5. Si des secrets sont dans votre historique Git, le retrait du fichier ne suffit pas — ils restent dans les commits antérieurs. Documentez la procédure :
    # le fichier retire reste dans l'historique :
    git log --all --full-history -- certs/ca.key
    
    # la reecriture d'historique est necessaire, et les secrets exposes
    # doivent de toute facon etre consideres comme compromis et regeneres
  6. Ajoutez le contrôle de plausibilité du TP 2 dans votre chaîne d'ingestion, en production.
  7. Rédigez le dossier de sécurité du système, en quatre parties : la surface d'attaque et les mesures en place ; le tableau d'audit avec ce qui est corrigé et ce qui est accepté ; la procédure de mise à jour, signature comprise ; et la conduite à tenir en cas de compromission d'un capteur.
  8. Rédigez enfin une note d'une page sur le passage en environnement industriel : ce qui changerait dans votre architecture si Thermeo équipait une usine plutôt qu'un immeuble de bureaux.
Questions de réflexion — à préparer par écrit
  • Quels constats avez-vous choisi de ne pas corriger, et sur quel argument ?
  • Un capteur est compromis. Décrivez les cinq premières minutes.
  • Votre dossier de sécurité serait-il exploitable par quelqu'un d'autre que vous ?
✓ terminé
Point de contrôle

Point de contrôle — fin du module M5

Jour 2 · Durcir et déployer · 20 min

Vérifiez avant de continuer
La signature de firmware fonctionne, et une modification d'un octet la fait échouer
Mon script de mise à jour traite les quatre scénarios, dont la version inférieure
Je sais expliquer l'attaque par retour en arrière et sa parade
Les trois constats les plus graves de mon audit sont corrigés
Mon API est authentifiée, avec comparaison à temps constant
La procédure de retrait des secrets de l'historique est documentée
Mon dossier de sécurité couvre les quatre parties attendues
Ma note sur le passage en industriel est rédigée
Aucune manipulation offensive n'a visé un système autre que le mien
Revue de fin de module — mercredi 19 août, 15 h 00

Votre encadrant lira votre dossier de sécurité comme le lirait un client. Un constat sans preuve, ou une correction annoncée sans vérification, sera relevé.

Le dernier critère n'est pas une formalité : la déontologie fait partie du métier, et un parcours de sécurité l'évalue.

Le projet fil rouge démarre jeudi 20 — deux jours pour assembler les cinq modules et préparer votre soutenance du vendredi 21.

✓ terminé

Mon avancement

Signature d'émargement

Demander de l'aide à votre encadrant

Étape concernée :

Avant d'envoyer

Cherchez seul pendant 30 minutes et notez ce que vous avez tenté. Ce que vous avez essayé fait partie de ce que vous présentez — c'est ce qui distingue l'autonomie de l'abandon.