hors suivi
Leçon

Modéliser la menace, et le faire tôt

Jour 1 · Modéliser la menace sur devsecops-lab · 25 min

Vous avez passé deux semaines à casser et réparer devsecops-lab : SAST, secrets, dépendances à CVE, image durcie, cluster. Vous connaissez ses dix failles par leur numéro. Le module d'aujourd'hui change de posture : au lieu de chercher des bugs un par un, vous allez raisonner sur l'architecture — les composants, les flux, les frontières — pour anticiper ce qui peut mal tourner, avant qu'un outil ne le confirme.

C'est cela, le threat modeling : un raisonnement structuré, mené tôt, sur ce qu'un attaquant pourrait faire de votre système. Il ne remplace ni le SAST ni le pentest — il les oriente. Un scanner lit le code ; un threat model lit les intentions de l'architecture, et voit des choses qu'aucun scanner ne verra jamais : une donnée sensible qui traverse une frontière sans contrôle, une hypothèse de confiance qui n'a jamais été écrite.

Le piège de ce module — à lever tout de suite

Un threat model se rate d'une seule façon : en le traitant comme un formulaire à remplir. Une table STRIDE de trente lignes recopiées d'un modèle générique ne vaut rien. Une table de huit lignes, dont chacune pointe une VULN réelle de votre appli et dit ce qu'on en fait, vaut un audit. Tout ce module est construit pour vous garder du côté du concret : chaque menace que vous écrirez devra se rattacher à une ligne de app.py.

Les quatre questions

Adam Shostack, qui a formalisé la méthode chez Microsoft, la ramène à quatre questions. Elles sont votre fil conducteur des deux jours :

QuestionCe qu'elle produitOù, dans ce module
Qu'est-ce qu'on construit ?Un schéma des composants et des flux : le DFDLeçon suivante et TP 1
Qu'est-ce qui peut mal tourner ?La liste des menaces, cadrée par STRIDETP 2 (STRIDE) et TP 3 (attack tree)
Qu'est-ce qu'on fait à ce sujet ?Priorisation (CVSS, DREAD) et traitementTP 4 et jour 2 (registre de risques)
A-t-on fait du bon travail ?On rejoue une attaque et on vérifie la détectionJour 2 (simulation d'incident)

La brique de départ : le diagramme de flux de données

On ne peut pas raisonner sur des menaces sans un schéma d'accord sur ce qu'on protège. Le DFD (Data Flow Diagram) est ce schéma. Il ne connaît que quatre types d'éléments — et c'est sa force : quatre formes suffisent à décrire n'importe quel système.

ÉlémentCe que c'estDans devsecops-lab
Entité externeCe qui interagit avec le système sans en faire partieLe client / navigateur
ProcessusCe qui transforme la donnéeL'appli Flask app.py, le sous-processus ping
Dépôt de donnéesCe qui stocke et restitue, sans transformerLa base SQLite lab.db, les secrets en dur
Flux de donnéesUne donnée qui circule d'un élément à un autreLa requête HTTP, la requête SQL, la commande shell

Le cinquième trait n'est pas un élément mais une ligne : la frontière de confiance. Elle marque l'endroit où la donnée change de main — où elle passe d'un contexte contrôlé à un contexte qui ne l'est pas, ou l'inverse. C'est là, et presque uniquement là, que vivent les menaces. Un flux qui reste à l'intérieur d'une même zone de confiance est rarement intéressant ; un flux qui traverse une frontière l'est toujours.

La leçon suivante superpose STRIDE à ce DFD : à chaque endroit où un flux franchit une frontière, elle donne un vocabulaire pour nommer ce qui peut mal tourner.

✓ terminé
Leçon

STRIDE, posé sur devsecops-lab

Jour 1 · Modéliser la menace sur devsecops-lab · 30 min

STRIDE est une liste de contrôle : six catégories de menaces, une par lettre. Son intérêt n'est pas d'être exhaustive — elle ne l'est pas — mais d'être rapide à poser sur un DFD. Devant chaque élément et chaque flux, vous vous demandez : « et si quelqu'un faisait ça ici ? », six fois, une fois par lettre.

La clé pour ne jamais confondre les catégories : chacune est la négation d'une propriété de sécurité. Retenez la propriété, vous retrouvez la menace.

LettreMenacePropriété niéeOù, dans devsecops-lab
SSpoofing — usurper une identitéAuthentification/login : l'injection SQL VULN-01 vous connecte en admin sans mot de passe
TTampering — altérer une donnéeIntégritéLa même SQLi écrit dans la base ; VULN-02 (XSS) altère la page rendue au client
RRepudiation — nier un acteTraçabilitéL'appli ne journalise quasiment rien : un acte malveillant ne laisse pas de trace exploitable
IInformation disclosure — divulguerConfidentialitéVULN-04 (IDOR) lit d'autres profils, VULN-08 (SSRF) lit des ressources internes, VULN-09 (debug) crache la pile
DDenial of service — rendre indisponibleDisponibilitéVULN-03 permet une commande coûteuse ; aucune limite de débit sur les routes
EElevation of privilege — s'éleverAutorisationVULN-03 et VULN-07 exécutent du code arbitraire ; le conteneur tourne en root (VULN-09)
frontière de confiance (réseau) Client entité externe Flask · app.py processus · :5000 SQLite · lab.db dépôt de données shell · ping (shell=True) processus · VULN-03 URL tierce cible SSRF /fetch · VULN-08 SECRET_KEY / API_KEY en dur · VULN-05 HTTP SQL · VULN-01 cmd GET url clé lue
Le DFD de devsecops-lab : quatre flux quittent le processus Flask, chacun franchit ou longe une frontière de confiance. Là où un flux la traverse, une menace vous attend — et une VULN connue s'y trouve déjà.
Une faille, plusieurs cases — c'est normal

L'injection SQL de /login apparaît en S (elle usurpe l'admin), en T (elle écrit en base) et pourrait figurer en E. Ne cherchez pas à la ranger dans une seule case : notez-la là où son effet est le plus grave — ici Spoofing, car le but de l'attaquant est de devenir admin — et mentionnez les autres. STRIDE est une grille de lecture, pas un tiroir à une place.

La menace qu'on oublie toujours : Repudiation

Regardez la colonne R. devsecops-lab n'écrit pratiquement aucun journal applicatif : le seul témoin des requêtes est le journal d'accès de Werkzeug. Conséquence directe : si un attaquant contourne l'authentification, vous ne pourrez pas reconstituer proprement ce qu'il a fait. Cette faiblesse ne casse rien aujourd'hui — c'est pourquoi on l'oublie — mais elle décide de tout au moment de l'incident. Vous la retrouverez lundi, quand vous jouerez une attaque sur votre propre maquette et tenterez d'en reconstruire la chronologie à partir de journaux qui n'existent qu'à moitié.

STRIDE, et les autres méthodes

STRIDE n'est pas seul. PASTA part du risque métier et remonte vers la technique ; on s'en servira demain comme grille de priorisation. LINDDUN vise la vie privée. Attack trees décomposent un objectif d'attaquant — c'est la leçon qui suit. On combine : STRIDE pour énumérer largement, l'attack tree pour approfondir un scénario qui fait peur.

Pour vous repérer

STRIDE se retient par sa symétrie : chaque lettre est la négation d'une propriété de sécurité. Spoofing nie l'authentification, Tampering l'intégrité, Repudiation la traçabilité, Information disclosure la confidentialité, Denial of service la disponibilité, Elevation of privilege l'autorisation. Si vous savez réciter la propriété niée, vous savez à quelle case rattacher une faille.

Le piège classique : ranger l'injection SQL dans « Tampering » parce qu'elle modifie la base. C'est vrai, mais son effet premier sur /login est de vous faire passer pour l'admin — donc Spoofing. Une même faille peut peupler plusieurs cases ; notez-la là où elle fait le plus de dégâts, puis mentionnez les autres.

✓ terminé
Quiz

Quiz — DFD, STRIDE et attack trees

Jour 1 · Modéliser la menace sur devsecops-lab · 10 min

1. Dans un DFD, qu'est-ce qui distingue une frontière de confiance d'un simple flux de données ?

2. L'injection SQL de /login (VULN-01) permet de se connecter en tant qu'admin sans mot de passe. À quelle catégorie STRIDE la rattacher en priorité ?

3. Pourquoi conduire le threat modeling au niveau de la conception, plutôt qu'une fois le pentest terminé ?

4. Dans le DFD de devsecops-lab, comment classer la base SQLite lab.db ?

5. Dans un attack tree, que signifie un nœud AND par rapport à un nœud OR ?

✓ terminé
Travaux pratiques

TP 1 — Le DFD de devsecops-lab

Jour 1 · Modéliser la menace sur devsecops-lab · 55 min

Dessiner le diagramme de flux de données de votre appli, avec ses frontières de confiance, et le poser dans le rapport de threat model.

Ce que vous produisez aujourd'hui

Le fichier rapports/threat-model.md. Il grandira jusqu'au point de contrôle : DFD (ce TP), table STRIDE (TP2), attack tree (TP3), findings notés en CVSS (TP4). C'est le livrable que M5 attaquera et que le PFR auditera — écrivez-le pour quelqu'un qui n'a pas votre code sous les yeux.

Le DFD s'écrit en Mermaid : du texte que GitHub rend en diagramme, versionnable, sans outil propriétaire (ni Microsoft Threat Modeling Tool, qui n'existe que sous Windows).

  1. Placez-vous à la racine de votre dépôt et créez le rapport. Ouvrez-le par le contexte, en une phrase — c'est la réponse à « qu'est-ce qu'on construit ? » :
    cd devsecops-lab
    mkdir -p rapports
    cat > rapports/threat-model.md <<'EOF'
    # Threat model - devsecops-lab
    
    Perimetre : application Flask locale (app.py, ~130 lignes), port 5000,
    base SQLite lab.db, deployee sur localhost uniquement.
    Objet du modele : anticiper les menaces avant exploitation (M5).
    
    ## 1. Ce qu'on construit - DFD
    EOF
  2. Listez d'abord, en clair, les éléments de chaque type. Ne dessinez rien encore : relisez app.py route par route et rangez chaque brique dans l'une des quatre catégories. Entités externes (client, URL tierce du /fetch), processus (Flask, le shell de /ping), dépôts (SQLite, les secrets en dur), flux (HTTP, SQL, commande shell, GET sortant).
  3. Écrivez le DFD en Mermaid, dans le rapport. Les deux sous-graphes matérialisent deux zones de confiance ; l'URL tierce reste dehors. Adaptez les libellés, mais gardez les quatre flux — ce sont eux qui portent les menaces :
    ```mermaid
    flowchart LR
      U[Client / navigateur]
      subgraph serveur[Zone serveur - localhost:5000]
        F([Flask app.py])
        DB[(SQLite lab.db)]
        SH([subprocess ping - shell=True])
        SEC[/SECRET_KEY et API_KEY en dur/]
      end
      EXT[URL tierce - cible SSRF]
      U -- requete HTTP --> F
      F -- requete SQL concatenee / VULN-01 --> DB
      F -- host non filtre / VULN-03 --> SH
      F -- GET url fournie / VULN-08 --> EXT
      SEC -. clef lue / VULN-05 .-> F
    ```
  4. Sous le diagramme, listez les frontières de confiance et, pour chaque flux qui en franchit une, la phrase « qui fait confiance à qui ». C'est le cœur du livrable :
    cat >> rapports/threat-model.md <<'EOF'
    
    ### Frontieres de confiance
    - FC1 reseau : Client -> Flask. Flask fait confiance a une entree HTTP non authentifiee.
    - FC2 processus : Flask -> shell. Flask fait confiance a une chaine construite depuis
      le parametre 'host' pour l'executer dans un shell.
    - FC3 donnees : Flask -> SQLite. Flask fait confiance a une requete concatenee.
    - FC4 sortant : Flask -> URL tierce. Flask fait confiance a une URL fournie par le client.
    - FC5 secret : les secrets ne devraient jamais quitter le serveur ; VULN-05 les expose
      dans la source versionnee.
    EOF
  5. Vérifiez le rendu. Ouvrez rapports/threat-model.md dans l'aperçu de votre éditeur ou poussez sur une branche pour voir GitHub rendre le Mermaid. Le diagramme doit s'afficher, pas rester du texte : si c'est du texte, le bloc n'est pas ouvert par ```mermaid.
  6. Committez le premier jet, sur une branche de travail (la main est protégée) :
    git checkout -b feat/threat-model
    git add rapports/threat-model.md
    git commit -m "threat model: DFD et frontieres de confiance de devsecops-lab"
Questions de réflexion — à préparer par écrit
  • Le flux Flask → shell franchit une frontière de processus. En une phrase, qui fait confiance à qui, et pourquoi cette confiance est-elle mal placée ?
  • Les secrets en dur (VULN-05) sont modélisés en dépôt de données interne. Quelle frontière leur exposition viole-t-elle, et à quel moment de la vie du dépôt ?
  • Votre DFD ne montre aucune frontière à l'intérieur de la zone serveur entre Flask et SQLite au sens réseau. Est-ce correct, ou manque-t-il une frontière de confiance ?
Pour vous repérer

Un DFD juste tient sur une page. Si le vôtre déborde, c'est que vous décrivez l'implémentation (fonctions, variables) au lieu des flux. Restez aux quatre formes : entité externe, processus, dépôt, flux — et une seule frontière par changement de contexte.

La question qui révèle un bon DFD : « pour chaque flux, qui fait confiance à qui, et pourquoi ? ». Le flux Flask → shell traverse une frontière de processus : Flask fait confiance à une chaîne construite depuis une entrée utilisateur. Écrivez cette phrase à côté de chaque flux qui franchit une frontière — c'est elle, pas le dessin, qui servira à STRIDE au TP suivant.

✓ terminé
Travaux pratiques

TP 2 — STRIDE, élément par élément

Jour 1 · Modéliser la menace sur devsecops-lab · 1 h 25

Dérouler STRIDE sur chaque élément et chaque flux du DFD, en reliant chaque menace retenue à une VULN réelle de votre code — ou à un « aucune » assumé.

STRIDE-per-element, en pratique

La méthode : pour chaque flux du DFD (pas chaque ligne de code), passez les six lettres en revue et demandez-vous si la menace est plausible ici. Quand elle l'est, nommez-la et rattachez-la à la VULN qui l'incarne. Quand elle ne l'est pas, écrivez pourquoi. Vous couvrez ainsi les dix VULN sans jamais partir d'une liste abstraite.

  1. Ouvrez d'abord votre première passe. Vous avez fait un STRIDE de découverte le 5 août, à la fin du module 1 : il est dans rapports/stride-devsecops-lab-m1.md. Relisez-le maintenant, avant d'écrire quoi que ce soit — puis mettez-le de côté. Vous le confronterez à votre tableau du jour à la dernière étape ; c'est la seule façon de mesurer ce que trois modules d'outillage ont changé à votre regard.
    cat rapports/stride-devsecops-lab-m1.md
  2. Ajoutez la section STRIDE au rapport, avec l'en-tête du tableau. Une ligne par menace retenue, rattachée à son flux et à sa VULN :
    cat >> rapports/threat-model.md <<'EOF'
    
    ## 2. Ce qui peut mal tourner - STRIDE
    
    | Element / flux | STRIDE | Menace concrete | VULN | Propriete niee | Etat |
    |---|---|---|---|---|---|
    EOF
  3. Remplissez le flux Client → Flask (FC1). C'est le plus riche : presque toutes les entrées passent par là. Exemple de lignes à écrire vous-même dans le tableau :
    | Client->Flask /login | S | usurper l'admin par SQLi         | VULN-01 | Authentification | corrige M1-J3 |
    | Client->Flask /search| T | injecter du HTML rendu tel quel  | VULN-02 | Integrite        | corrige M2 |
    | Client->Flask /profile| I| lire le profil d'un autre (IDOR) | VULN-04 | Confidentialite  | ouvert |
    | Client->Flask (tous)  | R| aucun journal applicatif exploitable | -   | Tracabilite      | ouvert |
  4. Déroulez maintenant les autres flux du DFD, un par un, et couvrez le reste des VULN. Chaque flux du TP1 doit produire au moins une ligne : Flask → shell (E/D, VULN-03), le flux /config vers le parseur YAML (E, VULN-07, déséralisation), Flask → URL tierce (I, VULN-08, SSRF), Flask → SQLite (T, VULN-01), secrets → Flask (I, VULN-05), et le hachage md5 de /hash (I, VULN-06).
  5. Traitez explicitement les cases vides. Ajoutez sous le tableau deux ou trois décisions de non-menace, argumentées — c'est ce qui prouve que vous avez réfléchi plutôt que rempli :
    cat >> rapports/threat-model.md <<'EOF'
    
    ### Cases laissees vides, et pourquoi
    - Spoofing sur Flask->SQLite : ecarte, la base ne porte pas d'identite a usurper ;
      la menace est en amont, sur la construction de la requete.
    - Denial of service sur secrets->Flask : ecarte, ce flux est interne et n'expose
      pas de surface reseau.
    EOF
  6. Relisez avec l'oeil de l'attaquant de M5 : chaque VULN-01 à VULN-10 doit apparaître au moins une fois dans la table ou dans les cases vides argumentées. Cochez-les sur la table des dix vulnérabilités. S'il en manque une, c'est un flux oublié au DFD — retournez au TP1.
  7. Reprenez maintenant votre passe du 5 août et comparez-la ligne à ligne avec celle du jour. Écrivez trois lignes de plus au rapport : ce que vous aviez vu et qui tient, ce que vous aviez manqué, et ce que vous aviez surestimé. La question utile n'est pas « ai-je progressé » mais « qu'est-ce qui, dans ce que j'ai appris depuis, m'a fait voir cette ligne-là ».
    cat >> rapports/threat-model.md <<'EOF'
    
    ### Ecart avec la premiere passe (stride-devsecops-lab-m1.md, 5 aout)
    - Tenu :
    - Manque a l'epoque :
    - Surestime a l'epoque :
    EOF
  8. Committez :
    git add rapports/threat-model.md
    git commit -m "threat model: table STRIDE reliee aux VULN-01..10"
Questions de réflexion — à préparer par écrit
  • Vous avez laissé au moins une case STRIDE vide en la justifiant. Pourquoi une case vide argumentée a-t-elle plus de valeur, pour un auditeur, qu'une case remplie par prudence ?
  • Repudiation ne correspond à aucune VULN figée mais figure dans votre table. En quoi est-ce malgré tout la menace la plus importante à documenter avant la simulation d'incident de lundi ?
  • VULN-09 (debug=True, en-têtes manquants) relève à la fois de Information disclosure et d'Elevation. Où l'avez-vous rangée, et pourquoi ce choix plutôt que l'autre ?
Pour vous repérer

La tentation est de remplir les six cases pour chaque élément. Résistez : une case vide justifiée (« pas de menace de spoofing sur ce flux car il ne porte aucune identité ») vaut mieux qu'une menace inventée. Un bon tableau STRIDE a des trous, et chaque trou est un choix, pas un oubli.

Test d'auto-vérification : chacune de vos lignes doit pointer soit une VULN-NN, soit une décision explicite. Si une ligne ne cite ni l'une ni l'autre, vous avez recopié un modèle générique — supprimez-la.

✓ terminé
Leçon

Attack trees : décomposer un objectif

Jour 1 · Modéliser la menace sur devsecops-lab · 20 min

STRIDE énumère largement. L'attack tree fait l'inverse : il part d'un objectif d'attaquant et le décompose en sous-objectifs, jusqu'à des actions concrètes. Là où STRIDE demande « qu'est-ce qui peut mal tourner partout ? », l'arbre demande « de combien de façons peut-on atteindre ce but précis ? ».

La structure

  • La racine est l'objectif de l'attaquant. Elle doit être mesurable : « exécuter une commande arbitraire sur le serveur », pas « pirater l'appli ».
  • Les nœuds intermédiaires sont des sous-objectifs.
  • Un nœud OR est atteint dès qu'un de ses enfants l'est. Un nœud AND exige que tous ses enfants le soient.
  • Les feuilles sont des actions concrètes — chez vous, des VULN avec leur charge d'exploitation.

La leçon de l'arbre est visuelle : sous une racine reliée par un OR, il suffit d'un seul chemin pour gagner. Corriger une branche ne protège de rien tant qu'une autre reste ouverte. C'est exactement le cas de devsecops-lab, où plusieurs failles indépendantes mènent au même résultat — l'exécution de code.

Un arbre, pas dix

On ne modélise pas tous les objectifs possibles : on choisit celui qui ferait le plus de dégâts. Pour devsecops-lab, c'est « exécuter du code arbitraire sur le serveur » — car de là, tout le reste tombe. Les feuilles seront VULN-01, VULN-03, VULN-05 et VULN-07 : quatre chemins distincts vers le même sommet, ce qui se lit d'un coup d'oeil sur l'arbre et jamais sur une table STRIDE.

Chaque feuille peut porter une étiquette : coût, probabilité, compétence requise. On reste qualitatif aujourd'hui. Demain, CVSS et DREAD donneront des chiffres à ces feuilles, et l'arbre deviendra un outil de priorisation.

✓ terminé
Travaux pratiques

TP 3 — Un attack tree vers l'exécution de code

Jour 1 · Modéliser la menace sur devsecops-lab · 55 min

Construire l'arbre d'attaque de l'objectif le plus grave, avec des feuilles qui sont des exploits réels de vos VULN.

Périmètre — rappel

Les charges que vous écrivez ici visent uniquement devsecops-lab et Juice Shop, sur votre poste. Ce sont des exploits réels ; comme posé en M1, ils ne s'emploient jamais ailleurs. L'arbre est un document d'analyse, pas une invitation à sortir du localhost.

  1. Ajoutez la section attack tree au rapport et posez la racine. Choisissez un objectif mesurable :
    cat >> rapports/threat-model.md <<'EOF'
    
    ## 3. Un scenario approfondi - attack tree
    
    Objectif racine : executer une commande arbitraire sur le serveur devsecops-lab.
    EOF
  2. Écrivez l'arbre en Mermaid. Notez le nœud OU à la racine (quatre chemins indépendants) et le nœud ET sous « détourner la confiance » (voler le secret et savoir signer un cookie) :
    ```mermaid
    flowchart TD
      G[But: executer une commande arbitraire sur le serveur]
      G --> OU{OU}
      OU --> C[Injecter une commande shell]
      OU --> D[Injecter un objet deserialise]
      OU --> B[Contourner l'authentification puis fonctions admin]
      OU --> T[Detourner la confiance de session]
      C --> C1[VULN-03: /ping?host=127.0.0.1;id]
      D --> D1[VULN-07: POST /config payload !!python/object/apply:os.system]
      B --> B1[VULN-01: user=admin'-- and pwd=x]
      T --> ET{ET}
      ET --> T1[VULN-05: lire SECRET_KEY dans la source]
      ET --> T2[forger un cookie de session signe avec cette clef]
    ```
  3. Sous l'arbre, expliquez en deux lignes pourquoi la racine est un OU : quatre chemins indépendants y mènent, donc corriger une seule branche ne ferme pas l'objectif. C'est la conclusion opérationnelle de l'arbre.
    cat >> rapports/threat-model.md <<'EOF'
    
    Lecture : la racine est un OU. Fermer VULN-03 seule laisse VULN-07, VULN-01 et le
    detournement de session ouverts. Il faut traiter les quatre branches, pas une.
    La branche 'session' est un ET : voler la clef ne suffit pas sans savoir signer.
    EOF
  4. Vérifiez que chaque feuille porte une charge exécutable, pas une catégorie. Relisez : /ping?host=127.0.0.1;id, la charge YAML, user=admin'--. Si une feuille ne se termine pas par quelque chose que vous pourriez taper, décomposez-la encore.
  5. Committez :
    git add rapports/threat-model.md
    git commit -m "threat model: attack tree vers l'execution de code"
Questions de réflexion — à préparer par écrit
  • La racine de votre arbre est reliée par un OU. Quelle conséquence concrète cela a-t-il sur une stratégie de correction qui traiterait les failles « une par une, par ordre de sévérité » ?
  • La branche « détourner la confiance de session » est un ET. Que se passerait-il si vous corrigiez VULN-05 (secret en dur) mais laissiez le mécanisme de signature inchangé ?
  • STRIDE et l'attack tree décrivent en partie les mêmes failles. Qu'apporte l'arbre que la table STRIDE ne montrait pas ?
Pour vous repérer

Une feuille d'attack tree n'est pas « injection SQL » : c'est user=admin'--&pwd=x. Descendez jusqu'à la charge que vous taperiez vraiment. Une feuille qu'on ne peut pas exécuter n'est pas une feuille, c'est un sous-objectif à décomposer davantage.

Gardez les charges de ce TP sous la main : ce sont exactement celles que vous rejouerez lundi dans la simulation d'incident, et que M5 portera sur Juice Shop.

✓ terminé
Leçon

Noter une faille : CVSS v3.1

Jour 1 · Modéliser la menace sur devsecops-lab · 25 min

« Cette faille est grave » n'est pas une note. CVSS v3.1 (Common Vulnerability Scoring System) transforme une appréciation en un nombre de 0 à 10, calculé à partir de huit métriques. Son intérêt n'est pas le chiffre seul mais le vecteur qui l'explique : une chaîne comme CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H dit pourquoi le score vaut 9.8, et permet à un tiers de refaire le calcul.

Le référentiel de ce parcours l'impose : chaque finding de votre rapport de pentest (M5) et de votre audit (PFR) portera son vecteur complet, pas seulement sa bande. On apprend donc à le construire ici, sur vos propres failles.

Les huit métriques du Base score

MétriqueValeursQuestion posée
AV — Attack VectorN / A / L / PD'où attaque-t-on ? Réseau (N), adjacent (A), local (L), physique (P)
AC — Attack ComplexityL / HL'attaque dépend-elle de conditions hors du contrôle de l'attaquant ?
PR — Privileges RequiredN / L / HQuels privilèges l'attaquant doit-il déjà posséder ?
UI — User InteractionN / RLa victime doit-elle faire quelque chose (cliquer, ouvrir) ?
S — ScopeU / CL'impact déborde-t-il du composant vulnérable (Changed) ou non (Unchanged) ?
C / I / AN / L / HImpact sur Confidentialité, Intégrité, Disponibilité

Les bandes de sévérité — CVSS v3.1

BandeScoreDélai de correction attendu
Critical9.0 – 10.0immédiat
High7.0 – 8.9≤ 7 jours
Medium4.0 – 6.9≤ 30 jours
Low0.1 – 3.9planifié
Info0.0à discrétion

Le cas de référence : l'injection SQL de /login

Déroulons VULN-01, métrique par métrique. C'est le vecteur donné en modèle par le référentiel, et celui que vous justifierez au TP suivant :

  • AV:N — exploitable par une simple requête HTTP à distance.
  • AC:L — aucune condition particulière ; la charge marche à tous les coups.
  • PR:N — aucun privilège requis : la route est publique.
  • UI:N — aucune action de la victime n'est nécessaire.
  • S:U — l'impact reste dans le composant attaqué (l'appli et sa base).
  • C:H / I:H / A:H — lecture, modification et destruction de la base sont toutes possibles.

Résultat : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 9.8, Critical.

Le contraste que vous allez mesurer

L'XSS réfléchi de /search (VULN-02) est aussi une injection. Pourtant son score tombe à 6.1, Medium. Deux différences l'expliquent : elle exige une action de la victime (UI:R — il faut qu'elle ouvre un lien piégé) et son Scope est Changed (S:C — le code s'exécute dans le navigateur, hors du serveur), mais elle n'atteint que partiellement la confidentialité et l'intégrité (C:L / I:L) et pas la disponibilité. Vous calculerez ce 6.1 vous-même au TP — c'est la meilleure preuve que CVSS discrimine, et que « tout n'est pas critique ».

On n'utilise que le Base score : c'est le seul comparable d'une organisation à l'autre. Les scores Temporal (maturité de l'exploit) et Environmental (votre contexte) existent, mais ne se partagent pas — on les laisse de côté.

Pour vous repérer

Vous connaissez déjà ce barème : vous l'avez lu le 7 août, en M2, pour trier les CVE de vos dépendances — le score venait alors de l'advisory, tout fait. Aujourd'hui vous le calculez, sur vos propres failles, qu'aucun advisory ne notera jamais à votre place. C'est le même vecteur et ce n'est pas le même geste.

Le réflexe faux : « c'est grave, donc 9 ». CVSS ne se devine pas, il se calcule à partir de huit métriques. Deux failles « graves » peuvent tomber à 9.8 et à 6.1 selon qu'elles exigent, ou non, une interaction de la victime et selon ce qu'elles atteignent en confidentialité, intégrité, disponibilité.

La métrique qui piège le plus souvent : Scope (S). S:C (Changed) veut dire que la faille déborde du composant vulnérable vers un autre — c'est le cas de l'XSS, qui s'exécute dans le navigateur de la victime, pas dans le serveur. Si vous hésitez entre S:U et S:C, demandez-vous : « l'impact sort-il du composant que j'attaque ? ».

✓ terminé
Travaux pratiques

TP 4 — Noter vos failles en CVSS

Jour 1 · Modéliser la menace sur devsecops-lab · 55 min

Calculer, à la main puis avec le calculateur officiel, le vecteur et le score CVSS de trois de vos failles, et vérifier que la note reflète bien l'écart de gravité.

L'outil

Le calculateur officiel est celui de FIRST (l'organisation qui maintient CVSS) : first.org/cvss/calculator/3.1. Il fonctionne hors-ligne une fois la page chargée et ne demande aucun compte. Vous cliquez les huit métriques, il affiche le score et le vecteur — mais c'est vous qui décidez des métriques.

  1. Ajoutez la section « findings notés » au rapport :
    cat >> rapports/threat-model.md <<'EOF'
    
    ## 4. Ce qu'on en fait - priorisation CVSS
    
    | VULN | Vecteur CVSS v3.1 | Score | Bande |
    |---|---|---|---|
    EOF
  2. Notez d'abord VULN-01 (SQLi) sans regarder le modèle : posez les huit métriques une par une en vous justifiant à voix haute, puis lisez le score. Vous devez retomber sur le vecteur de référence :
    | VULN-01 | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H | 9.8 | Critical |
  3. Notez maintenant VULN-02 (XSS réfléchi). Attention aux deux métriques qui la distinguent : UI:R (la victime doit ouvrir un lien piégé) et S:C (le code s'exécute dans son navigateur, hors du serveur). Impacts partiels : C:L / I:L / A:N. Vérifiez que vous obtenez bien 6.1 :
    | VULN-02 | CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N | 6.1 | Medium |
  4. Notez enfin VULN-03 (injection de commande). Raisonnez seul, puis comparez : elle s'attaque par le réseau, sans privilège ni interaction, et donne l'exécution de code — donc un impact total. Elle doit retomber au niveau de la SQLi :
    | VULN-03 | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H | 9.8 | Critical |
  5. Écrivez, sous le tableau, une phrase qui explique l'écart entre l'XSS et les deux autres. C'est la trace que vous avez compris, pas seulement calculé :
    cat >> rapports/threat-model.md <<'EOF'
    
    L'XSS (6.1) est plus basse que la SQLi et l'injection de commande (9.8) parce qu'elle
    exige une action de la victime (UI:R) et n'atteint que partiellement C et I, sans A.
    Elle deborde en revanche du serveur (S:C), ce qui l'empeche de tomber plus bas.
    EOF
  6. Committez :
    git add rapports/threat-model.md
    git commit -m "threat model: notation CVSS de VULN-01, VULN-02, VULN-03"
Questions de réflexion — à préparer par écrit
  • VULN-01 et VULN-03 partagent exactement le même vecteur CVSS (9.8). Est-ce à dire qu'elles présentent le même risque pour votre appli ? Qu'est-ce que CVSS ne capture pas ?
  • Vous avez peut-être noté l'XSS S:U au premier essai. Qu'est-ce qui, dans la définition de Scope, impose S:C pour une XSS réfléchie ?
  • Un score CVSS est censé être reproductible par un tiers. Deux analystes peuvent-ils aboutir à des vecteurs différents pour la même faille ? Sur quelles métriques, et est-ce un défaut de la méthode ?
Pour vous repérer

Construisez le vecteur avant de regarder le score. Si vous partez du chiffre, vous ajusterez les métriques pour tomber dessus et le vecteur ne voudra plus rien dire. L'ordre est : huit décisions, puis le score qui en tombe.

Le calculateur FIRST ne fait que l'arithmétique — il ne juge pas à votre place si l'XSS est S:C ou S:U. C'est vous qui décidez du vecteur ; lui donne le nombre. Un score que vous ne savez pas défendre lettre à lettre est un score faux, même s'il vient du calculateur.

✓ terminé
Travaux pratiques

TP 5 — Boucler le threat model

Jour 1 · Modéliser la menace sur devsecops-lab · 40 min

Assembler et relire threat-model.md pour qu'il tienne debout seul, sans votre code sous les yeux — c'est le livrable que le point de contrôle exige.

Ce que couvre ce TP

Vous avez tout écrit aux TP1 à 4. Il reste à en faire un document qui tient debout : un sommaire, des renvois cohérents entre le DFD, STRIDE, l'arbre et les notes CVSS, et un résumé de tête. C'est ce document, et non vos souvenirs, que Samer lira ce soir.

  1. Ajoutez un résumé exécutif en tête du rapport (juste après le titre) : trois phrases qu'un décideur non technique comprend. Combien de failles, la plus grave, l'état général :
    # Threat model - devsecops-lab
    
    > Resume : 10 failles modelisees, dont 3 Critical (SQLi, injection de commande,
    > deserialisation). L'objectif d'attaque le plus grave - executer du code - est
    > atteignable par 4 chemins independants. Faiblesse structurelle : absence de
    > journalisation exploitable (Repudiation), qui compromet toute reponse a incident.
  2. Relisez en croisant les sections. Chaque VULN citée dans le DFD ou l'attack tree apparaît-elle dans la table STRIDE ? Les trois failles notées en CVSS sont-elles les plus graves de STRIDE ? Corrigez les écarts — c'est le vrai travail de ce TP.
  3. Faites le test du lecteur sans code : reprenez la ligne VULN-01 et vérifiez que, sans ouvrir app.py, on comprend où est la faille, comment la déclencher, et pourquoi elle vaut 9.8. Ajoutez la charge d'exploitation si elle manque.
  4. Vérifiez une dernière fois le rendu des deux diagrammes Mermaid dans l'aperçu, puis poussez la branche :
    git add rapports/threat-model.md
    git commit -m "threat model: resume executif et relecture croisee"
    git push -u origin feat/threat-model
Questions de réflexion — à préparer par écrit
  • Votre résumé exécutif tient en trois phrases. Laquelle des quatre sections du rapport un décideur lira-t-il en réalité, et qu'est-ce que cela dit de ce qu'il faut y soigner ?
  • Le rapport doit se lire sans le code. Qu'avez-vous dû ajouter pour cela, que vous trouviez « évident » parce que vous connaissez app.py par coeur ?
Pour vous repérer

Le test du livrable : donnez-le mentalement à quelqu'un qui n'a jamais vu app.py. S'il ne peut pas, à sa seule lecture, rejouer VULN-01 et comprendre pourquoi elle est notée 9.8, le rapport n'est pas fini. Ce n'est pas la longueur qui compte, c'est l'auto-suffisance.

✓ terminé
Point de contrôle

M4 · J1 — DFD, STRIDE et attack tree sur devsecops-lab

Jour 1 · Modéliser la menace sur devsecops-lab · 30 min

Vérifiez avant de continuer
Ce que vous présentez au point de 16 h 30

Votre encadrant vous demandera de montrer, pas de réciter. Ayez threat-model.md ouvert et son rendu Mermaid visible. Il vous fera probablement recalculer une note CVSS de tête et désigner une case STRIDE que vous avez choisi de laisser vide.

Gardez sous la main les charges d'exploitation de votre attack tree : ce sont elles que vous rejouerez lundi, sur votre propre maquette, dans l'exercice d'incident.

✓ terminé
Leçon

Du modèle au registre de risques

Jour 2 · Du risque à l'incident rejoué · 30 min

Vous savez noter une faille. Noter une faille n'est pas gérer un risque. CVSS vous dit « cette injection SQL vaut 9.8 dans l'absolu » ; le registre de risques répond à la question que se pose vraiment une organisation : « compte tenu de notre contexte, qu'est-ce qu'on en fait, qui s'en occupe, et pour quand ? ».

Le registre reprend donc la note CVSS, mais y ajoute ce que CVSS ignore volontairement — parce que CVSS se veut comparable entre organisations, et que votre contexte ne l'est pas.

ColonneOrigineCe qu'elle apporte
VULN, CWE, OWASP, STRIDEThreat model (J1)L'identité de la faille
Vecteur et score CVSSTP4 (J1)La gravité intrinsèque, comparable
Score DREADNouveau (ci-dessous)La priorité métier, pour arbitrer entre égaux
TraitementDécisionMitiger / accepter / transférer / éviter
Propriétaire, échéance, statutDécisionQui, pour quand, où on en est

DREAD — une grille complémentaire, jamais un remplacement

DREAD cote cinq dimensions, chacune de 1 à 3 ici (faible, moyen, fort). Son total, de 5 à 15, sert à départager deux risques que CVSS place au même niveau — pas à remplacer CVSS, qui reste la note de référence du parcours.

LettreDimensionQuestion
DDamageQuels dégâts si l'attaque réussit ?
RReproducibilityEst-elle facile à rejouer à l'identique ?
EExploitabilityQuel effort, quelle compétence pour l'exploiter ?
AAffected usersCombien d'utilisateurs touchés ?
DDiscoverabilityEst-elle facile à découvrir ?
DREAD est subjectif — c'est su, et c'est encadré

Deux analystes peuvent coter DREAD différemment : ses axes ne sont pas normalisés, contrairement au vecteur CVSS qu'un tiers recalcule à l'identique. C'est pourquoi le référentiel de ce parcours en fait une grille de priorisation métier complémentaire, jamais la note de sévérité. Concrètement : la sévérité, c'est CVSS ; DREAD sert à décider lequel de deux 9.8 vous corrigez d'abord.

Les quatre traitements — et le faux cinquième

  • Mitiger — corriger, ou réduire la probabilité ou l'impact. Le cas général.
  • Accepter — décider, en connaissance de cause et par écrit, de vivre avec. Une acceptation a toujours un propriétaire et une raison.
  • Transférer — faire porter le risque par un tiers (assurance, prestataire). Le risque ne disparaît pas, il change de porteur.
  • Éviter — supprimer la fonctionnalité qui porte le risque. Radical, parfois juste.

« Ignorer » n'est pas dans la liste. Ignorer, c'est accepter sans le documenter — et c'est précisément ce qu'un audit relève. Toute ligne de votre registre portera donc un traitement explicite, jamais un blanc.

Pour vous repérer

CVSS note la faille ; le registre de risques note le risque pour vous — ce n'est pas la même chose. Une faille 9.8 sur une route désactivée en production est un risque faible. Le registre ajoute donc à CVSS deux dimensions que CVSS ignore : la probabilité dans votre contexte, et ce que vous décidez d'en faire.

Quatre traitements possibles, pas un de plus : mitiger (corriger ou réduire), accepter (décider de vivre avec, en le documentant), transférer (assurance, prestataire), éviter (supprimer la fonctionnalité). « Ignorer » n'est pas un traitement — c'est accepter sans le dire, et c'est ce qu'un audit sanctionne.

✓ terminé
Quiz

Quiz — CVSS, DREAD et gestion du risque

Jour 2 · Du risque à l'incident rejoué · 10 min

1. VULN-01 est notée CVSS 9.8. Dans quelle bande tombe-t-elle, et qu'implique cette bande ?

2. Dans le vecteur de VULN-01, pourquoi la métrique Attack Vector vaut-elle AV:N (Network) ?

3. Le référentiel impose CVSS comme note de référence et n'admet DREAD qu'en complément. Pourquoi ne pas s'appuyer sur DREAD seul ?

4. Pourquoi VULN-02 (XSS réfléchi) obtient-elle 6.1 quand VULN-01 (SQLi) obtient 9.8, alors que les deux sont des injections ?

5. Dans un registre de risques, que signifie exactement « accepter » un risque ?

✓ terminé
Travaux pratiques

TP 6 — Le registre de risques

Jour 2 · Du risque à l'incident rejoué · 1 h

Construire risk-register.csv : les dix VULN, leur note CVSS, une cote DREAD, un traitement décidé et un propriétaire.

Où va le fichier

rapports/risk-register.csv, à côté du threat model. M5 s'en servira pour prioriser l'exploitation, le PFR pour montrer que chaque faille a un traitement décidé. C'est un document vivant : on ne supprime pas une ligne corrigée, on en change le statut.

  1. Créez le fichier avec son en-tête. Une colonne par information ; les vecteurs CVSS ne contiennent pas de virgule, ce qui garde le CSV propre :
    cat > rapports/risk-register.csv <<'EOF'
    id,titre,composant,cwe,owasp,stride,cvss_vecteur,cvss_score,cvss_bande,dread,traitement,statut,module,proprietaire
    EOF
  2. Ajoutez les premières lignes. Reprenez les vecteurs calculés hier pour VULN-01/02/03 ; notez que VULN-01 et VULN-02 sont déjà corrigées (statut), et servez-vous de DREAD pour départager les deux 9.8 :
    cat >> rapports/risk-register.csv <<'EOF'
    VULN-01,Injection SQL login,POST /login,CWE-89,A03,Spoofing,CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H,9.8,Critical,15,mitiger,corrige,M1-J3,Elie
    VULN-02,XSS reflechi,GET /search,CWE-79,A03,Tampering,CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N,6.1,Medium,9,mitiger,corrige,M2,Elie
    VULN-03,Injection de commande,GET /ping,CWE-78,A03,Elevation,CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H,9.8,Critical,13,mitiger,corrige,M2,Elie
    EOF
  3. Complétez les sept lignes restantes (VULN-04 à VULN-10) vous-même, en vous appuyant sur la table des dix vulnérabilités du parcours pour le CWE, l'OWASP et le module de correction. Calculez chaque vecteur CVSS comme hier. Repères : IDOR (VULN-04) et SSRF (VULN-08) sont encore ouvertes, elles ne seront traitées qu'en M5 ; les CVE des dépendances (VULN-10) relèvent de la SCA de M2.
  4. Décidez un traitement pour chaque ligne, et justifiez au moins une acceptation. Sur une maquette locale, certaines menaces (par exemple l'absence de rate-limiting, ou une faille sur une route jamais exposée) peuvent être acceptées avec raison — à condition de l'écrire. Ajoutez ces justifications dans un fichier compagnon :
    cat > rapports/risk-register-decisions.md <<'EOF'
    # Decisions de traitement - justifications
    
    - VULN-XX : ACCEPTEE. Motif : route non exposee sur la maquette locale, impact nul
      hors localhost. Proprietaire : Elie. A revoir si l'appli est exposee.
    EOF
  5. Vérifiez que le CSV est bien formé — dix lignes de données, aucune virgule parasite :
    column -s, -t rapports/risk-register.csv | less -S
    # ou ouvrez-le dans un tableur ; toutes les colonnes doivent s'aligner
  6. Committez le registre et ses justifications :
    git add rapports/risk-register.csv rapports/risk-register-decisions.md
    git commit -m "risk register: 10 VULN notees, traitees et attribuees"
Questions de réflexion — à préparer par écrit
  • VULN-01 et VULN-03 ont le même score CVSS (9.8). Sur quel axe DREAD les avez-vous départagées, et pourquoi ce choix se défend-il ?
  • Vous avez « accepté » au moins un risque. Qu'est-ce qui distingue votre acceptation documentée d'un simple oubli, aux yeux d'un auditeur ?
  • VULN-04 (IDOR) et VULN-08 (SSRF) restent ouvertes jusqu'à M5. Est-il cohérent de les laisser « ouvertes » dans un registre, ou faudrait-il un statut intermédiaire ?
Pour vous repérer

Le registre n'est pas la table CVSS d'hier recopiée. Deux colonnes le distinguent et sont l'objet du TP : le traitement (mitiger/accepter/transférer/éviter) et le statut (ouvert/corrigé/en cours). Une ligne sans traitement décidé n'est pas une ligne de registre, c'est une observation.

Le format CSV n'est pas un caprice : un registre se trie, se filtre, se diffe dans Git. Gardez un séparateur propre et pas de virgule dans les cellules — d'où les vecteurs CVSS qui n'en contiennent pas.

✓ terminé
Leçon

Répondre à un incident

Jour 2 · Du risque à l'incident rejoué · 30 min

Un threat model dit ce qui peut arriver. La réponse à incident traite ce qui arrive. Les deux se répondent : un incident bien géré part toujours d'une préparation faite à froid — les journaux qu'on a pensé à collecter, les playbooks qu'on a écrits avant d'en avoir besoin. C'est la quatrième question de Shostack retournée : « a-t-on fait du bon travail ? » se vérifie le jour où quelqu'un attaque.

1 · Préparation outils, journaux, playbooks 2 · Détection et analyse qu'est-il arrivé ? 3 · Confinement, éradication, reprise arrêter puis réparer 4 · Post-incident leçons, corrections les leçons nourrissent la préparation du prochain incident
Les quatre phases de la réponse à incident (NIST SP 800-61). La boucle compte autant que les phases : un incident dont on ne tire rien se reproduit.

Les quatre phases (NIST SP 800-61)

  1. Préparation. Avant tout incident : journalisation en place, playbooks écrits, rôles connus, sauvegardes. C'est la phase qui décide du succès des trois autres.
  2. Détection et analyse. Repérer qu'il se passe quelque chose, comprendre quoi, jusqu'où. Sans journaux, cette phase est aveugle.
  3. Confinement, éradication et reprise. Arrêter la propagation, retirer la cause, puis remettre en service sans revenir à l'état vulnérable.
  4. Activité post-incident. Ce qu'on a appris, ce qu'on change. La boucle qui évite la répétition.
PICERL, la variante à six étapes

Le cadre SANS découpe le même cycle en six : Preparation, Identification, Containment, Eradication, Recovery, Lessons learned. Plus granulaire, il se prête mieux à l'écriture d'un playbook, où chaque étape devient une section. On l'utilisera pour structurer le vôtre ; retenez que c'est le même cycle que NIST, découpé plus fin.

Ce qu'est un playbook

Un playbook n'est pas un cours : c'est une procédure faite pour être suivie sous pression, par quelqu'un qui n'a pas le temps de réfléchir. D'où sa forme : un déclencheur clair, des étapes numérotées, des décisions binaires (« si le secret a été poussé sur GitHub → le révoquer immédiatement »), et pas de prose. Vous en écrirez un au TP suivant, puis vous le suivrez pour de vrai dans la simulation.

Le lien avec la Repudiation de STRIDE

Rappelez-vous la case R de votre table STRIDE : devsecops-lab ne journalise presque rien. La phase de détection et analyse repose entièrement sur les journaux. Une appli qui ne journalise pas rend la réponse à incident aveugle — vous allez le vivre dans quelques minutes, en tentant de reconstruire une attaque à partir des seules lignes de Werkzeug. C'est là que la menace la plus discrète de votre threat model montre son vrai coût.

✓ terminé
Travaux pratiques

TP 7 — Écrire un playbook

Jour 2 · Du risque à l'incident rejoué · 55 min

Rédiger un playbook de réponse à incident suivant les six étapes PICERL, pour un scénario tiré de vos propres failles.

Le scénario retenu

On écrit le playbook du secret exposé (VULN-05) : un identifiant poussé dans le dépôt, détecté par Gitleaks. C'est le cas où l'éradication est la plus instructive — un secret entré dans l'historique Git n'en sort pas en effaçant une ligne. Vous suivrez ce playbook, en partie, dans la simulation qui suit.

  1. Créez le playbook et posez son en-tête : déclencheur, périmètre, gravité attendue. Le déclencheur doit être sans ambiguïté :
    cat > rapports/playbook-secret-expose.md <<'EOF'
    # Playbook - secret expose dans le depot (VULN-05)
    
    Declencheur : Gitleaks (pre-commit ou CI) signale un secret, OU une clef en clair
    est reperee dans la source / l'historique.
    Perimetre : depot devsecops-lab. Gravite : elevee (un secret pousse est compromis).
    EOF
  2. Écrivez les six sections PICERL, à l'impératif, avec des décisions binaires. Voici la trame ; complétez chaque étape avec la commande exacte :
    cat >> rapports/playbook-secret-expose.md <<'EOF'
    
    ## 1. Preparation (a froid, deja en place)
    - Hook Gitleaks en pre-commit + job 'secrets' en CI (M1).
    - .gitignore couvre .env ; .env.exemple versionne a vide.
    
    ## 2. Identification
    - [ ] Confirmer : quel secret, quelle valeur, depuis quel commit ?
          git log -p -S '<fragment_du_secret>' -- <fichier>
    - [ ] Le secret a-t-il ete POUSSE sur le distant ? (si oui -> compromis definitif)
    
    ## 3. Containment
    - [ ] Si pousse : considerer la clef comme publique des maintenant.
    - [ ] Bloquer la fuite en cours : ne plus committer ; geler la branche.
    
    ## 4. Eradication (deux gestes distincts, dans cet ordre)
    - [ ] REVOQUER la clef chez le fournisseur (elle ne doit plus rien valoir).
    - [ ] PURGER l'historique : git filter-repo --invert-paths --path <fichier>
          (ou BFG) ; retirer la valeur du code, la remplacer par une variable d'env.
    
    ## 5. Recovery
    - [ ] Re-scanner : gitleaks detect --no-git ; doit ne rien trouver.
    - [ ] Emettre une NOUVELLE clef, la stocker hors du depot (.env non versionne).
    - [ ] Forcer le re-clone chez les collaborateurs (l'historique a change).
    
    ## 6. Lessons learned
    - [ ] Pourquoi le hook n'a pas bloque ? (installe ? contourne par --no-verify ?)
    - [ ] Consigner l'incident dans journal-incidents.md.
    EOF
  3. Relisez avec l'oeil de quelqu'un sous pression : chaque case à cocher est-elle une action unique et sans ambiguïté ? Réécrivez toute étape qui contient un « il faudrait » ou un « selon le cas » sans dire lequel.
  4. Committez le playbook :
    git add rapports/playbook-secret-expose.md
    git commit -m "playbook: reponse au secret expose (VULN-05), forme PICERL"
Questions de réflexion — à préparer par écrit
  • Votre playbook sépare « révoquer la clé » et « purger l'historique ». Pourquoi l'ordre compte-t-il, et que se passe-t-il si on purge sans révoquer ?
  • La reprise contient un re-scan Gitleaks. Sans lui, comment sauriez-vous que l'éradication a réussi ? Que prouve exactement ce re-scan, et que ne prouve-t-il pas ?
  • Un playbook est fait pour être suivi sous pression. Quelle étape du vôtre risque le plus d'être bâclée le jour J, et comment l'avez-vous rendue difficile à sauter ?
Pour vous repérer

Le test d'un bon playbook : donnez-le à quelqu'un de stressé qui n'a pas participé à l'écriture. S'il hésite sur une étape, c'est qu'elle contient de la prose au lieu d'une action. Numérotez, impérativez, tranchez.

Écrivez le playbook avant la simulation, pas pendant. Tout l'intérêt de l'exercice de tout à l'heure est de suivre une procédure écrite à froid — et de constater où elle vous laisse tomber.

✓ terminé
Travaux pratiques

TP 8 — Jouer l'incident sur votre maquette

Jour 2 · Du risque à l'incident rejoué · 1 h 10

Rejouer une attaque réelle sur votre propre devsecops-lab, la détecter dans les journaux, appliquer le cadre de réponse et reconstruire la chronologie — en mesurant ce que l'absence de journalisation vous coûte.

Périmètre — la règle de droit du parcours

Comme posé en M1 : vous n'attaquez que ce que vous déployez vous-même, sur votre poste. Cet exercice se déroule entièrement sur localhost, sur votre maquette devsecops-lab. C'est précisément parce que la cible est la vôtre que rejouer l'attaque est légal. Rien, ici, ne sort de votre machine.

  1. Déployez la version vulnérable de l'appli — l'incident n'a pas de sens sur le code corrigé. Utilisez l'image :dev construite en M1, ou revenez au commit d'avant-correction sur une branche jetable :
    docker run -d --name lab-incident --network dso-net -p 5000:5000 devsecops-lab:dev
    # alternative : git checkout -b incident-demo <commit_avant_fix_VULN-01>
  2. Dans un terminal, suivez les journaux du conteneur — c'est votre unique source d'analyste. Laissez-le ouvert :
    docker logs -f lab-incident
  3. Casquette attaquant. Rejouez la charge SQLi de votre attack tree contre votre propre appli. Notez l'heure exacte :
    date +%H:%M:%S
    curl -s -X POST http://localhost:5000/login --data "user=admin'--&pwd=x"
    # reponse attendue : bienvenue (HTTP 200) sans mot de passe valide
  4. Enchaînez une deuxième action pour épaissir l'incident : lisez un profil (IDOR, VULN-04) ou testez la commande (VULN-03). Notez encore l'heure :
    curl -s http://localhost:5000/profile/1
    curl -s "http://localhost:5000/ping?host=127.0.0.1;id"
  5. Casquette analyste. Vous n'avez que le terminal des journaux. Sans vous servir de ce que vous savez, reconstruisez ce que les logs permettent d'affirmer : quelles requêtes, à quelle heure, quel code de retour. Écrivez cette chronologie dans un rapport d'incident :
    cat > rapports/incident-2026-08-17.md <<'EOF'
    # Incident (exercice) - 2026-08-17
    
    ## Chronologie reconstruite depuis les seuls journaux
    | Heure | Ce que le journal montre | Ce qu'il ne montre PAS |
    |-------|--------------------------|------------------------|
    | hh:mm | POST /login -> 200       | le corps : la charge d'injection est invisible |
    | hh:mm | GET /profile/1 -> 200    | si d'autres id ont ete enumerees |
    | hh:mm | GET /ping -> 200         | la commande reellement executee |
    EOF
  6. Faites l'inventaire du manque. Sous la table, listez les questions auxquelles vous ne pouvez pas répondre avec les journaux actuels, et reliez-les à la case Repudiation de votre threat model :
    cat >> rapports/incident-2026-08-17.md <<'EOF'
    
    ## Ce que l'access log ne permet pas de conclure
    - Impossible de prouver la charge SQLi : le corps du POST n'est pas journalise.
    - Impossible de savoir quelles donnees ont ete lues (pas de log applicatif).
    - Lien direct avec STRIDE-Repudiation (threat-model.md, section 2).
    
    ## Recommandation
    Journalisation applicative structuree au niveau des routes (qui, quoi, resultat),
    distincte de l'access log Werkzeug. Sans elle, la detection reste aveugle.
    EOF
  7. Appliquez, pour la forme, la fin du cycle : l'éradication de VULN-01 a déjà eu lieu en M1-J3 (requête paramétrée). Vérifiez-le en rejouant la charge sur la version corrigée, qui doit renvoyer 401 — c'est votre étape de reprise. Puis arrêtez le conteneur d'incident :
    docker stop lab-incident && docker rm lab-incident
  8. Consignez l'incident dans le journal du dépôt et committez :
    git add rapports/incident-2026-08-17.md
    git commit -m "incident (exercice): SQLi rejouee, chronologie et angle mort de journalisation"
Questions de réflexion — à préparer par écrit
  • Vous avez reconstruit la chronologie à partir de l'access log seul. Quelle est la chose la plus importante que vous n'avez pas pu prouver, et pourquoi est-ce grave dans un vrai incident (au-delà de la technique) ?
  • La case Repudiation de votre threat model, écrite vendredi, s'est matérialisée aujourd'hui. En quoi cet exercice change-t-il la façon dont vous auriez priorisé cette menace dans le registre de risques ?
  • Votre playbook du TP précédent portait sur le secret exposé, pas sur la SQLi. À la lumière de cet exercice, quelle section d'un playbook « intrusion applicative » serait la plus dure à exécuter sur devsecops-lab en l'état ?
Pour vous repérer

Le résultat le plus important de ce TP n'est pas « l'attaque marche » — vous le savez déjà. C'est ce que vous ne trouvez pas dans les journaux. Notez chaque question à laquelle les logs de Werkzeug ne répondent pas : quel corps de requête, quelle donnée exfiltrée, quelle session. Cette liste de trous est votre vraie découverte.

Vous jouez les deux rôles : attaquant (vous connaissez la charge, c'est celle de votre attack tree) puis analyste (vous ne disposez que des journaux). Séparez bien les deux casquettes — l'analyste n'a pas le droit de « savoir » ce que l'attaquant a fait.

✓ terminé
Travaux pratiques

TP 9 — Boucler les livrables du module

Jour 2 · Du risque à l'incident rejoué · 45 min

Rendre cohérents et rejouables les trois livrables du module — threat model, registre, playbook et incident — avant la revue de fin de module.

Les livrables à aligner

Quatre fichiers dans rapports/ : threat-model.md, risk-register.csv (+ ses décisions), playbook-secret-expose.md et incident-2026-08-17.md. La revue de cet après-midi vérifie qu'ils racontent la même histoire.

  1. Passe de cohérence. Vérifiez, VULN par VULN, que le threat model et le registre s'accordent : même score CVSS, même statut. Vérifiez que l'incident rejoué (SQLi) et le playbook renvoient à des menaces présentes dans le threat model. Corrigez les écarts.
  2. Mettez à jour journal-incidents.md : ajoutez la ligne de l'exercice d'aujourd'hui (symptôme, cause, correction, temps). Le journal est la mémoire du dépôt, il doit refléter la simulation.
    # ajoutez une ligne au tableau existant, par exemple :
    # | 2026-08-17 | SQLi rejouee en exercice | access log insuffisant | reco journalisation applicative | 0 |
  3. Complétez reponses-reflexion.md : une section par TP du module, la question recopiée, votre réponse en trois à cinq lignes. C'est là que Samer puisera ses questions cet après-midi.
  4. Ouvrez la Pull Request vers main. La branche est protégée : la PR est le seul chemin, et elle déclenche les cinq jobs que vous avez construits depuis le 3 août — secrets et sast (M1), sca et dast (M2), image (M3). Vérifiez qu'ils passent tous au vert :
    git add -A
    git commit -m "M4: alignement des livrables, journal et reflexions a jour"
    git push
    # puis ouvrez la PR feat/threat-model -> main sur GitHub
Questions de réflexion — à préparer par écrit
  • En alignant le registre et le threat model, avez-vous trouvé une incohérence ? Laquelle, et qu'est-ce qu'elle révèle sur la façon dont vous avez travaillé pendant deux jours ?
  • La PR vers main déclenche les cinq portes du pipeline. Pourquoi est-il pertinent qu'un livrable documentaire passe malgré tout par les mêmes garde-fous que du code ?
Pour vous repérer

La cohérence entre livrables est ce qu'on regarde en revue : une VULN notée 9.8 dans le threat model mais absente du registre, ou un incident qui invoque une menace jamais modélisée, sont les défauts qui sautent aux yeux. Faites la passe croisée maintenant, pas pendant la revue.

✓ terminé
Point de contrôle

M4 · Bilan — Threat model, registre de risques et incident rejoué

Jour 2 · Du risque à l'incident rejoué · 2 h

Vérifiez avant de continuer
La revue de fin de module

Elle a lieu lundi à 15 h 00 et dure deux heures — c'est la durée portée par ce point de contrôle. Votre encadrant partira de votre reponses-reflexion.md et de vos quatre livrables ; il vous fera rejouer l'incident et défendre vos décisions de traitement.

Ce qui compte n'est pas la longueur des documents, mais qu'ils racontent la même histoire et que vous sachiez la défendre page fermée. Le threat model et le registre que vous validez aujourd'hui sont les entrées directes du pentest de M5 : vous ne repartez pas de zéro demain.

✓ 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.