Modélisation des menaces#

Avertissement

Ce chapitre est conceptuel et pédagogique. Tous les exemples d’attaque présentés sont fictifs et construits à des fins d’illustration. Aucun système réel n’est ciblé. La modélisation des menaces est une activité défensive : elle vise à anticiper les risques pour mieux les contenir.

La modélisation des menaces (threat modeling) est l’activité qui consiste à identifier, classer et prioriser les risques de sécurité d’un système avant qu’ils ne se concrétisent. Contrairement aux tests d’intrusion (qui évaluent ce qui existe), la modélisation des menaces intervient en amont — idéalement dès la conception — pour orienter les choix d’architecture.

Les prérequis de ce chapitre sont : une familiarité avec les architectures microservices, les protocoles HTTP/TLS, et les bases STRIDE abordées dans architecture/20_securite_conception.md.

Hide code cell source

import matplotlib.pyplot as plt
import matplotlib.patches as patches
import numpy as np
import seaborn as sns
import networkx as nx
from matplotlib.patches import FancyBboxPatch

STRIDE appliqué aux microservices#

STRIDE est un acronyme mnémotechnique créé par Microsoft pour catégoriser les menaces sur un système. Chaque lettre correspond à une propriété de sécurité violée :

Lettre

Menace

Propriété violée

S

Spoofing (usurpation d’identité)

Authenticité

T

Tampering (altération)

Intégrité

R

Repudiation (répudiation)

Non-répudiation

I

Information Disclosure (divulgation)

Confidentialité

D

Denial of Service (déni de service)

Disponibilité

E

Elevation of Privilege (élévation de privilèges)

Autorisation

Application à un système microservices concret#

Considérons une plateforme e-commerce composée de :

  • Un API Gateway exposé sur Internet (entrée unique)

  • Un service Auth (JWT, OAuth 2.0)

  • Un service Orders (commandes, base PostgreSQL)

  • Un service Payments (intégration PSP externe)

  • Un bus de messages Kafka interne

  • Un service Notifications (email/SMS)

Pour chaque composant, on applique STRIDE systématiquement :

API Gateway

  • Spoofing : un attaquant forge un en-tête X-User-Id si le gateway ne valide pas les tokens en amont.

  • Denial of Service : flood HTTP, slowloris, amplification.

  • Information Disclosure : stack traces exposées dans les réponses d’erreur.

Service Auth

  • Spoofing : clé de signature JWT faible ou exposée → forge de tokens valides.

  • Elevation of Privilege : injection dans le champ role du payload JWT si la validation est absente.

  • Repudiation : absence de logs d’authentification → impossibilité d’auditer les connexions.

Kafka (bus interne)

  • Tampering : un service compromis publie des messages falsifiés sans signature de message.

  • Information Disclosure : topics non chiffrés contenant des données personnelles.

  • Spoofing : absence d’authentification mTLS entre producteurs et courtiers.

Service Payments

  • Tampering : modification du montant entre le service Orders et le service Payments si l’intégrité du message n’est pas vérifiée (HMAC manquant).

  • Information Disclosure : numéros de carte stockés en clair dans les logs.

DFD — Diagramme de flux de données

STRIDE s’applique sur les éléments d’un DFD (Data Flow Diagram) : processus, flux de données, entités externes et zones de confiance (trust boundaries). L’outil pytm (Python Threat Modeling) permet de générer des DFD et d’automatiser l’application de STRIDE.

Arbres d’attaque#

Un arbre d’attaque formalise les chemins qu’un attaquant peut emprunter pour atteindre un objectif. La racine est l’objectif de l’attaquant ; les nœuds internes décomposent cet objectif en sous-objectifs ; les feuilles représentent des actions atomiques.

Nœuds AND / OR#

  • Nœud OR : l’attaquant réussit si au moins un fils est réalisé. Il choisira le chemin de moindre résistance.

  • Nœud AND : l’attaquant doit réaliser tous les fils. Le coût total est la somme des coûts fils.

Coût d’attaque#

Chaque feuille peut être annotée par :

  • Coût (temps, argent, compétence requise)

  • Probabilité de succès

  • Détectabilité

Ces métriques permettent de prioriser les contre-mesures : on traite en priorité les feuilles peu coûteuses et peu détectables.

Exemple : arbre d’attaque contre une API web#

Objectif racine : Exfiltrer la base clients

[OR] Exfiltrer la base clients
├── [AND] Injection SQL directe
│   ├── Identifier un endpoint vulnérable (fuzzing)
│   └── Injecter une requête UNION SELECT
├── [AND] Compromission d'un compte admin
│   ├── [OR] Obtenir les credentials admin
│   │   ├── Phishing ciblé (spear phishing)
│   │   └── Credential stuffing (liste de fuites)
│   └── Contourner le MFA
│       ├── SIM swapping (téléphonie)
│       └── Phishing OTP en temps réel
└── [AND] Accès direct à la base via un service interne
    ├── Compromettre un pod Kubernetes interne
    └── Pivoter vers le réseau de la base de données

La cellule suivante visualise cet arbre avec NetworkX.

sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)

G = nx.DiGraph()

nodes = {
    "root":     ("Exfiltrer\nla base clients", "OR",  "#e74c3c"),
    "sqli":     ("Injection SQL\ndirecte",       "AND", "#e67e22"),
    "sqli1":    ("Identifier endpoint\nvulnérable", "leaf", "#95a5a6"),
    "sqli2":    ("Injecter\nUNION SELECT",       "leaf", "#95a5a6"),
    "admin":    ("Compromettre\ncompte admin",    "AND", "#e67e22"),
    "creds":    ("Obtenir credentials\nadmin",    "OR",  "#f39c12"),
    "creds1":   ("Spear phishing",               "leaf", "#95a5a6"),
    "creds2":   ("Credential\nstuffing",         "leaf", "#95a5a6"),
    "mfa":      ("Contourner\nle MFA",           "OR",  "#f39c12"),
    "mfa1":     ("SIM swapping",                 "leaf", "#95a5a6"),
    "mfa2":     ("Phishing OTP\ntemps réel",     "leaf", "#95a5a6"),
    "pivot":    ("Accès direct\nvia service interne","AND","#e67e22"),
    "pivot1":   ("Compromettre\npod Kubernetes",  "leaf", "#95a5a6"),
    "pivot2":   ("Pivoter vers\nréseau DB",       "leaf", "#95a5a6"),
}

edges = [
    ("root", "sqli"), ("root", "admin"), ("root", "pivot"),
    ("sqli", "sqli1"), ("sqli", "sqli2"),
    ("admin", "creds"), ("admin", "mfa"),
    ("creds", "creds1"), ("creds", "creds2"),
    ("mfa", "mfa1"), ("mfa", "mfa2"),
    ("pivot", "pivot1"), ("pivot", "pivot2"),
]

G.add_nodes_from(nodes.keys())
G.add_edges_from(edges)

pos = {
    "root":   (4, 6),
    "sqli":   (1, 4.5), "admin":  (4, 4.5), "pivot":  (7, 4.5),
    "sqli1":  (0, 3),   "sqli2":  (2, 3),
    "creds":  (3, 3),   "mfa":    (5, 3),
    "creds1": (2.2, 1.5),"creds2":(3.8, 1.5),
    "mfa1":   (4.5, 1.5),"mfa2":  (5.8, 1.5),
    "pivot1": (6.3, 3), "pivot2": (7.8, 3),
}

fig, ax = plt.subplots(figsize=(13, 7))
ax.set_xlim(-0.5, 9)
ax.set_ylim(0.5, 7.5)
ax.axis("off")
ax.set_title("Arbre d'attaque — Exfiltration de la base clients", fontsize=13, fontweight="bold", pad=14)

for edge in edges:
    x0, y0 = pos[edge[0]]
    x1, y1 = pos[edge[1]]
    ax.annotate("", xy=(x1, y1 + 0.3), xytext=(x0, y0 - 0.3),
                arrowprops=dict(arrowstyle="-|>", color="#7f8c8d", lw=1.2))

for nid, (label, ntype, color) in nodes.items():
    x, y = pos[nid]
    shape = "round,pad=0.15" if ntype != "leaf" else "square,pad=0.12"
    bbox = FancyBboxPatch((x - 0.85, y - 0.28), 1.7, 0.56,
                          boxstyle=shape, linewidth=1.5,
                          edgecolor="#2c3e50", facecolor=color, alpha=0.88)
    ax.add_patch(bbox)
    tag = {"OR": " [OR]", "AND": " [AND]", "leaf": ""}.get(ntype, "")
    ax.text(x, y, label + tag, ha="center", va="center",
            fontsize=7.2, color="white" if ntype != "leaf" else "#2c3e50",
            fontweight="bold" if ntype != "leaf" else "normal")

plt.savefig("attack_tree.png", dpi=120, bbox_inches="tight")
plt.show()
_images/d056d54905a81ac9e077bee08deb4636d4c8f37bf81bdec23248a643f55b1829.png

MITRE ATT&CK#

MITRE ATT&CK (Adversarial Tactics, Techniques, and Common Knowledge) est une base de connaissances ouverte qui catalogue les tactiques et techniques utilisées par des acteurs malveillants réels. Elle est organisée en matrices selon le domaine d’application : Enterprise, Mobile, ICS.

Structure de la matrice Enterprise#

La matrice Enterprise compte 14 tactiques ordonnées chronologiquement selon la progression d’une attaque :

  1. Reconnaissance

  2. Resource Development

  3. Initial Access

  4. Execution

  5. Persistence

  6. Privilege Escalation

  7. Defense Evasion

  8. Credential Access

  9. Discovery

  10. Lateral Movement

  11. Collection

  12. Command and Control

  13. Exfiltration

  14. Impact

Chaque tactique regroupe des techniques (ex. T1190 — Exploit Public-Facing Application) qui peuvent avoir des sous-techniques (ex. T1190.001).

Mapping sur un scénario : compromission d’une API web#

Voici comment mapper une attaque fictive sur ATT&CK :

Phase

Tactique

Technique

ID

1

Reconnaissance

Active Scanning — Vulnerability Scanning

T1595.002

2

Initial Access

Exploit Public-Facing Application

T1190

3

Execution

Command and Scripting Interpreter

T1059

4

Persistence

Web Shell

T1505.003

5

Credential Access

OS Credential Dumping

T1003

6

Lateral Movement

Remote Services — SSH

T1021.004

7

Collection

Data from Local System

T1005

8

Exfiltration

Exfiltration Over Web Service

T1567

La cellule suivante simule une heatmap de fréquence des techniques par tactique.

sns.set_theme(style="whitegrid", palette="muted", font_scale=1.0)

tactics = [
    "Reconnaissance", "Resource Dev.", "Initial Access", "Execution",
    "Persistence", "Privilege Esc.", "Defense Evasion", "Credential Access",
    "Discovery", "Lateral Movement", "Collection", "C2",
    "Exfiltration", "Impact"
]

techniques = [
    "T-alpha", "T-beta", "T-gamma", "T-delta", "T-epsilon"
]

rng = np.random.default_rng(42)
freq = rng.integers(0, 25, size=(len(tactics), len(techniques))).astype(float)

# Renforcer quelques cases pour illustrer un scénario réaliste
hot = [(2, 0), (2, 2), (3, 1), (4, 4), (6, 0), (6, 3), (7, 2), (9, 1), (12, 0), (13, 2)]
for r, c in hot:
    freq[r, c] = rng.integers(60, 95)

fig, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(
    freq,
    xticklabels=techniques,
    yticklabels=tactics,
    annot=True, fmt=".0f",
    cmap="YlOrRd",
    linewidths=0.4,
    linecolor="#ecf0f1",
    cbar_kws={"label": "Fréquence simulée"},
    ax=ax
)
ax.set_title("Heatmap MITRE ATT&CK — Fréquence simulée par tactique/technique", fontsize=12, fontweight="bold")
ax.set_xlabel("Technique représentative", fontsize=10)
ax.set_ylabel("Tactique", fontsize=10)
ax.tick_params(axis="x", rotation=0)
ax.tick_params(axis="y", rotation=0)
plt.savefig("mitre_heatmap.png", dpi=120, bbox_inches="tight")
plt.show()
_images/7a5260ebdcca1aba416af343c19fa4c6a5d58e6601a2bf97ea9f69701c25d140.png
sns.set_theme(style="whitegrid", palette="muted", font_scale=1.0)

tactics_clean = [
    "Reconnaissance", "Resource Dev.", "Initial Access", "Execution",
    "Persistence", "Privilege Esc.", "Defense Evasion", "Credential Access",
    "Discovery", "Lateral Movement", "Collection", "C2",
    "Exfiltration", "Impact"
]
techniques_clean = ["T-alpha", "T-beta", "T-gamma", "T-delta", "T-epsilon"]

rng = np.random.default_rng(42)
freq = rng.integers(0, 25, size=(len(tactics_clean), len(techniques_clean))).astype(float)
hot = [(2, 0), (2, 2), (3, 1), (4, 4), (6, 0), (6, 3), (7, 2), (9, 1), (12, 0), (13, 2)]
for r, c in hot:
    freq[r, c] = rng.integers(60, 95)

fig, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(
    freq,
    xticklabels=techniques_clean,
    yticklabels=tactics_clean,
    annot=True, fmt=".0f",
    cmap="YlOrRd",
    linewidths=0.4,
    linecolor="#ecf0f1",
    cbar_kws={"label": "Fréquence simulée"},
    ax=ax
)
ax.set_title("Heatmap MITRE ATT&CK — Fréquence simulée par tactique/technique", fontsize=12, fontweight="bold")
ax.set_xlabel("Technique représentative", fontsize=10)
ax.set_ylabel("Tactique", fontsize=10)
ax.tick_params(axis="x", rotation=0)
ax.tick_params(axis="y", rotation=0)
plt.savefig("mitre_heatmap.png", dpi=120, bbox_inches="tight")
plt.show()
_images/7a5260ebdcca1aba416af343c19fa4c6a5d58e6601a2bf97ea9f69701c25d140.png

Extrait JSON STIX (format d’échange ATT&CK)#

{
  "type": "attack-pattern",
  "spec_version": "2.1",
  "id": "attack-pattern--3f886f2a-13fc-4027-81c5-6b2cc7f2a0fc",
  "name": "Exploit Public-Facing Application",
  "external_references": [
    {
      "source_name": "mitre-attack",
      "external_id": "T1190",
      "url": "https://attack.mitre.org/techniques/T1190"
    }
  ],
  "kill_chain_phases": [
    {
      "kill_chain_name": "mitre-attack",
      "phase_name": "initial-access"
    }
  ],
  "x_mitre_platforms": ["Linux", "Windows", "macOS", "Network"],
  "x_mitre_data_sources": ["Application Log: Application Log Content"]
}

DREAD et CVSS v3.1#

DREAD#

DREAD est un modèle de scoring qualitatif (anciennement utilisé chez Microsoft) composé de 5 critères, chacun noté de 1 à 10 :

Critère

Signification

Damage

Impact si l’attaque réussit

Reproducibility

Facilité à reproduire l’attaque

Exploitability

Compétence requise pour exploiter

Affected users

Proportion d’utilisateurs impactés

Discoverability

Facilité à découvrir la vulnérabilité

Score DREAD = moyenne des 5 critères. Simple mais subjectif — CVSS lui est préféré pour la rigueur.

CVSS v3.1#

Le Common Vulnerability Scoring System v3.1 fournit un score objectif sur 10 à partir de vecteurs formalisés.

Métriques de base (Base Score) :

Vecteur

Valeurs

Signification

AV (Attack Vector)

N/A/L/P

Network, Adjacent, Local, Physical

AC (Attack Complexity)

L/H

Low, High

PR (Privileges Required)

N/L/H

None, Low, High

UI (User Interaction)

N/R

None, Required

S (Scope)

U/C

Unchanged, Changed

C (Confidentiality)

N/L/H

None, Low, High

I (Integrity)

N/L/H

None, Low, High

A (Availability)

N/L/H

None, Low, High

Le vecteur CVSS s’écrit : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H → Score 10.0 (critique).

Métriques temporelles (Temporal) : exploitabilité, niveau de remédiation, confiance dans le rapport.

Métriques d’environnement (Environmental) : ajustement selon le contexte opérationnel.

sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)

# Simulation CVSS v3.1 radar — 4 vulnérabilités fictives
categories = ["AV (réseau)", "AC (faible)", "PR (aucun)", "UI (aucune)", "Conf. H", "Intég. H", "Dispo. H"]
N = len(categories)
angles = np.linspace(0, 2 * np.pi, N, endpoint=False).tolist()
angles += angles[:1]

vulns = {
    "CVE-xxxx-0001\n(SQL Injection)": [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5],
    "CVE-xxxx-0002\n(SSRF interne)":  [1.0, 0.5, 0.5, 1.0, 1.0, 0.5, 0.5],
    "CVE-xxxx-0003\n(XSS réfléchi)":  [1.0, 1.0, 0.5, 0.5, 0.5, 0.5, 0.0],
    "CVE-xxxx-0004\n(LPE local)":     [0.2, 1.0, 0.5, 0.0, 0.5, 1.0, 0.5],
}
colors = ["#e74c3c", "#e67e22", "#3498db", "#2ecc71"]

fig, ax = plt.subplots(figsize=(7, 7), subplot_kw=dict(polar=True))
ax.set_theta_offset(np.pi / 2)
ax.set_theta_direction(-1)
ax.set_xticks(angles[:-1])
ax.set_xticklabels(categories, fontsize=9)
ax.set_ylim(0, 1.2)
ax.set_yticks([0.25, 0.5, 0.75, 1.0])
ax.set_yticklabels(["0.25", "0.50", "0.75", "1.00"], fontsize=7)
ax.set_title("Radar CVSS v3.1 — 4 vulnérabilités fictives", fontsize=12, fontweight="bold", pad=20)

for (label, values), color in zip(vulns.items(), colors):
    vals = values + values[:1]
    ax.plot(angles, vals, color=color, linewidth=2, label=label.replace("\n", " "))
    ax.fill(angles, vals, alpha=0.12, color=color)

ax.legend(loc="upper right", bbox_to_anchor=(1.35, 1.15), fontsize=8)
plt.savefig("cvss_radar.png", dpi=120, bbox_inches="tight")
plt.show()
_images/8fbcddcad99cbeadb617c857a1ca50e8b20154463f2f3170e7dc7efcedc49055.png

PASTA — Process for Attack Simulation and Threat Analysis#

PASTA est une méthodologie de threat modeling orientée risque en 7 étapes, qui aligne la vision technique et la vision métier.

Étape 1 — Définir les objectifs métier et de sécurité Exemple : « Protéger les données de paiement des clients, garantir la disponibilité du tunnel de commande. »

Étape 2 — Définir la portée technique Inventaire des composants, flux de données, technologies. On produit un DFD de niveau 0.

Étape 3 — Décomposer l’application DFD détaillé (niveaux 1 et 2), identification des zones de confiance, des points d’entrée.

Étape 4 — Analyser les menaces On consulte les bases (CVE, MITRE ATT&CK, NVD) pour identifier les menaces applicables à chaque composant.

Étape 5 — Identifier les vulnérabilités On mappe les menaces sur les vulnérabilités existantes via des scans (SAST, DAST, SCA). Cette étape produit une liste hiérarchisée.

Étape 6 — Modéliser les attaques (arbres d’attaque) Pour chaque vulnérabilité critique, on construit un arbre d’attaque comme présenté en section 2.

Étape 7 — Analyser le risque résiduel et définir les contre-mesures On calcule le risque = probabilité × impact, on identifie les contrôles existants, on quantifie le risque résiduel et on priorise les actions correctives.

PASTA vs STRIDE

STRIDE est orienté composants (on parcourt les éléments du DFD). PASTA est orientée risque métier (on part des objectifs business et on descend vers le technique). Dans la pratique, les deux approches se complètent : STRIDE pour énumérer exhaustivement les menaces par composant, PASTA pour prioriser en fonction du contexte métier.

Threat modeling en pratique#

Quand modéliser les menaces ?#

La modélisation des menaces est la plus efficace (et la moins coûteuse) lorsqu’elle est réalisée :

  • Lors de la conception d’une nouvelle fonctionnalité ou d’un nouveau service

  • Lors de changements architecturaux significatifs (ajout d’un composant, changement de modèle d’authentification)

  • Lors de la migration vers un nouveau provider cloud ou un nouveau paradigme (microservices, serverless)

  • Périodiquement (revue annuelle ou après un incident de sécurité)

Qui implique-t-on ?#

Un threat model réussi réunit des perspectives complémentaires :

Rôle

Apport

Architecte

Vue d’ensemble du système, contraintes techniques

Développeur

Détails d’implémentation, connaissance du code

Ops / SRE

Connaissance de l’infrastructure, surface d’attaque réseau

Security Engineer

Connaissance des techniques d’attaque, MITRE ATT&CK

Product Owner

Contexte métier, criticité des données

Artefacts produits#

À l’issue d’une session de threat modeling, on documente :

  • Le DFD annoté avec les zones de confiance

  • La liste STRIDE : pour chaque composant, les menaces applicables

  • Les arbres d’attaque pour les scénarios critiques

  • La liste des contrôles : contrôles existants et contrôles recommandés

  • Le registre de risques : risque brut, contrôles, risque résiduel, propriétaire, date de revue

Exemple de threat model YAML#

threat_model:
  system: "API e-commerce"
  date: "2026-03-01"
  version: "1.0"
  components:
    - name: api_gateway
      type: process
      trust_boundary: internet_edge
      threats:
        - id: T-001
          category: STRIDE-D
          description: "Flood HTTP saturant les workers WSGI"
          likelihood: high
          impact: high
          controls:
            existing: ["rate limiting nginx (100 req/s)"]
            recommended: ["WAF avec règles DDoS L7", "Circuit breaker Envoy"]
          risk_score: 8.5
          owner: infra_team
          review_date: "2026-09-01"
        - id: T-002
          category: STRIDE-S
          description: "Forge d'en-tête X-User-Id si JWT non validé"
          likelihood: medium
          impact: critical
          controls:
            existing: []
            recommended: ["Valider signature JWT dans le gateway", "Supprimer X-User-Id des requêtes entrantes"]
          risk_score: 9.0
          owner: dev_team
          review_date: "2026-04-15"

Outils de threat modeling

  • OWASP Threat Dragon : outil open-source avec interface graphique pour dessiner des DFD et annoter les menaces STRIDE.

  • pytm : bibliothèque Python qui génère des DFD, applique STRIDE et produit des rapports HTML.

  • Microsoft Threat Modeling Tool : outil desktop avec des templates pour Azure.

  • IriusRisk / ThreatModeler : plateformes commerciales intégrées aux pipelines CI/CD.

Résumé#

  1. STRIDE structure l’analyse des menaces en 6 catégories liées aux propriétés de sécurité fondamentales ; il s’applique composant par composant sur un DFD annoté avec les zones de confiance.

  2. Les arbres d’attaque modélisent les chemins d’attaque sous forme hiérarchique AND/OR ; l’annotation par coût et probabilité permet de prioriser les contre-mesures.

  3. MITRE ATT&CK est la référence industrielle pour mapper les comportements adversariaux réels sur 14 tactiques et des centaines de techniques, avec un format d’échange STIX/JSON interopérable.

  4. CVSS v3.1 fournit un score objectif sur 10 basé sur des vecteurs formalisés (AV, AC, PR, UI, S, C, I, A) ; les métriques temporelles et environnementales permettent d’adapter le score au contexte.

  5. DREAD est un modèle qualitatif plus simple, utile pour des évaluations rapides, mais moins rigoureux que CVSS et peu adapté aux comparaisons inter-organisations.

  6. PASTA aligne la modélisation des menaces sur les objectifs métier en 7 étapes, depuis les objectifs business jusqu’aux arbres d’attaque et à l’analyse du risque résiduel.

  7. STRIDE et PASTA se complètent : STRIDE pour l’exhaustivité par composant, PASTA pour la priorisation métier.

  8. La modélisation des menaces est un artefact vivant : elle doit être mise à jour lors de changements architecturaux et revue périodiquement, idéalement intégrée dans le pipeline CI/CD (pytm, IriusRisk).

  9. Les artefacts produits (DFD annoté, liste STRIDE, arbres d’attaque, registre de risques) constituent la documentation de sécurité de référence pour l’équipe et les auditeurs externes.

  10. L’approche shift-left impose de réaliser la modélisation des menaces en phase de conception, avant que le code soit écrit, pour minimiser le coût des corrections.