22. Hardening systèmes et benchmarks CIS#
Le hardening consiste à réduire la surface d’attaque d’un système en appliquant un ensemble de configurations sécurisées, en désactivant les fonctionnalités inutiles et en renforçant les mécanismes d’authentification et de contrôle d’accès. Les benchmarks CIS (Center for Internet Security) fournissent la référence industrielle la plus utilisée pour guider ces efforts.
CIS Benchmarks#
Le CIS publie des guides de configuration pour plus de 100 technologies : systèmes d’exploitation, bases de données, navigateurs, cloud, conteneurs, équipements réseau.
Structure : Niveau 1 et Niveau 2#
Niveau |
Profil |
Description |
|---|---|---|
Niveau 1 |
Pratique standard |
Recommandations applicables à tout environnement, impact opérationnel minimal, conformité large |
Niveau 2 |
Haute sécurité |
Recommandations pour environnements à haute sensibilité (défense, finance), peut impacter l’usabilité |
CIS Benchmark Ubuntu Linux — catégories principales#
Système de fichiers : montage avec options restrictives (noexec, nodev, nosuid), désactivation des systèmes de fichiers inutiles (cramfs, squashfs, udf).
Packages et services : suppression des services non nécessaires (CUPS, Avahi, NFS si non utilisé), désactivation du démarrage automatique.
Réseau : paramètres sysctl (désactivation du routage IP, protection SYN flood, désactivation des redirections ICMP), pare-feu UFW/nftables.
Authentification : politique de mots de passe (/etc/login.defs), verrouillage après échecs (PAM faillock), SSH durci (PermitRootLogin no, PasswordAuthentication no, Protocol 2).
Audit : auditd configuré pour tracer les accès privilégiés, modifications de fichiers sensibles, connexions.
Intégrité : AIDE (Advanced Intrusion Detection Environment) ou Tripwire pour la surveillance des modifications de fichiers.
CIS Benchmark Docker#
Image de base minimale (distroless ou Alpine)
Daemon Docker non exposé sur TCP sans TLS
Conteneurs exécutés sans
--privilegedUtilisateur non-root dans le conteneur
Capabilities limitées (
--cap-drop=ALL --cap-add=...)Lecture seule du système de fichiers racine (
--read-only)Profil seccomp et AppArmor activés
Pas de montage du socket Docker dans les conteneurs
CIS Benchmark Kubernetes#
RBAC activé et principe du moindre privilège
Network Policies pour segmenter les pods
PodSecurityAdmission (successeur de PodSecurityPolicy)
Secrets chiffrés au repos dans etcd
Audit logging du serveur API
Rotation des certificats
Namespaces isolés par équipe/application
Lynis — audit de sécurité Linux#
Lynis est un outil d’audit de sécurité open-source pour les systèmes Unix/Linux. Il vérifie plusieurs centaines de points de contrôle et produit un score de durcissement (Hardening Index, 0 à 100).
# Installation
apt install lynis # ou téléchargement depuis cisofy.com
# Audit système complet
lynis audit system
# Audit silencieux (pour scripts/CI)
lynis audit system --quiet --no-colors
# Audit avec profil personnalisé
lynis audit system --profile /etc/lynis/custom.prf
# Voir les résultats
lynis show details KRNL-5820 # Détails d'un test spécifique
Catégories auditées :
AUTH: authentification (PAM, sudo, SSH)NETW: réseau (interfaces, services ouverts, pare-feu)FILE: intégrité des fichiers et permissionsMALW: présence d’outils antimalwareCRYP: configuration TLS/SSLKRNL: paramètres kernel (sysctl)LOGG: configuration des logs et auditd
Interprétation du Hardening Index :
0 – 49 : sécurité insuffisante
50 – 74 : niveau acceptable pour environnement non critique
75 – 89 : bon niveau, hardening avancé en place
90 – 100 : niveau haute sécurité (rare en production)
Lynis en CI/CD
Intégrer Lynis dans un pipeline CI pour valider les images de base avant déploiement. Un score en dessous d’un seuil configuré bloque le déploiement. Utilisez lynis audit system --pentest pour une analyse non-root plus complète.
OpenSCAP — conformité automatisée#
SCAP (Security Content Automation Protocol) est un standard NIST pour l’automatisation de la vérification de conformité.
Composants SCAP :
XCCDF (Extensible Configuration Checklist Description Format) : liste de vérification structurée
OVAL (Open Vulnerability and Assessment Language) : tests de vérification automatisés
CPE (Common Platform Enumeration) : identification standardisée des plateformes
# Installation
dnf install openscap-scanner scap-security-guide # RHEL/Fedora
apt install libopenscap8 ssg-debderived # Debian/Ubuntu
# Lister les profils disponibles
oscap info /usr/share/xml/scap/ssg/content/ssg-ubuntu2204-ds.xml
# Évaluation contre le profil CIS Niveau 1
oscap xccdf eval \
--profile xccdf_org.ssgproject.content_profile_cis_level1_server \
--results /tmp/scan-results.xml \
--report /tmp/scan-report.html \
/usr/share/xml/scap/ssg/content/ssg-ubuntu2204-ds.xml
# Évaluation DISA STIG
oscap xccdf eval \
--profile xccdf_org.ssgproject.content_profile_stig \
--results /tmp/stig-results.xml \
--report /tmp/stig-report.html \
/usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml
# Générer un script de remédiation Bash
oscap xccdf generate fix \
--fix-type bash \
--result-id "" \
--output /tmp/remediation.sh \
/tmp/scan-results.xml
Cycle de gestion des vulnérabilités#
La gestion des vulnérabilités est un processus continu, pas ponctuel.
Les cinq phases#
1. Discovery (Découverte) Inventaire complet des actifs (CMDB) et scan de vulnérabilités : Nessus, Qualys, OpenVAS/GVM, Trivy (conteneurs).
2. Assessment (Évaluation) Identification et qualification des vulnérabilités : CVE, score CVSS v3.1, contexte de l’actif, exposition réseau.
3. Prioritization (Priorisation) Tri par criticité réelle (CVSS + EPSS + contexte business), pas seulement par score brut.
4. Remediation (Remédiation) Patch, workaround, ou acceptation de risque documentée. Suivi des SLA par sévérité.
5. Verification (Vérification) Re-scan post-patch pour confirmer la résolution. Fermeture du ticket de vulnérabilité.
EPSS — Exploit Prediction Scoring System#
L’EPSS (Exploit Prediction Scoring System), maintenu par FIRST, estime la probabilité qu’une vulnérabilité soit activement exploitée dans les 30 prochains jours.
Complémentarité CVSS / EPSS :
Situation |
CVSS |
EPSS |
Action |
|---|---|---|---|
Score élevé, forte probabilité |
9.8 |
0.92 |
Patch immédiat (P1) |
Score élevé, faible probabilité |
9.1 |
0.003 |
Patch planifié (P2) |
Score moyen, forte probabilité |
6.5 |
0.78 |
Remonter en priorité |
Score moyen, faible probabilité |
6.2 |
0.001 |
Patch lors de la prochaine fenêtre |
Le CVSS mesure la sévérité potentielle ; l’EPSS mesure l’imminence de l’exploitation. Les deux dimensions sont nécessaires pour une priorisation rationnelle.
Gestion des patchs#
SLA par sévérité (bonnes pratiques)#
CVSS CRITICAL (9.0-10.0) : patch ou workaround sous 24 heures
CVSS HIGH (7.0-8.9) : patch sous 7 jours
CVSS MEDIUM (4.0-6.9) : patch sous 30 jours
CVSS LOW (0.1-3.9) : patch lors de la prochaine fenêtre planifiée
Stratégies de déploiement#
Canary patching : déploiement du patch sur 5 % des systèmes, surveillance 24-48h, déploiement progressif si pas de régression.
Fenêtres de maintenance : créneaux planifiés (souvent dimanche 02h-06h) pour minimiser l’impact opérationnel. Certains patchs critiques peuvent nécessiter une fenêtre d’urgence.
Rollback plan : chaque patch doit avoir un plan de retour arrière documenté et testé. Pour les systèmes critiques, snapshot VM avant chaque patch.
Dépendance entre patchs et tests
Un patch système peut briser des applications. Établir un environnement de staging représentatif et une suite de tests de non-régression automatisés avant de déployer en production.
Hardening vs usabilité : compromis opérationnels#
Chaque mesure de hardening a un coût opérationnel. La matrice de décision aide à prioriser les contrôles.
Contrôle |
Sécurité gagnée |
Coût opérationnel |
Recommandé |
|---|---|---|---|
MFA sur comptes admin |
Très élevée |
Faible |
Oui, toujours |
Désactivation USB |
Élevée |
Moyen (shadow IT) |
Selon contexte |
|
Élevée |
Faible |
Oui |
AppArmor/SELinux |
Élevée |
Élevé (tuning) |
Oui, avec planning |
Audit syscalls complet |
Moyenne |
Élevé (I/O) |
Sélectif |
Pare-feu applicatif WAF |
Élevée |
Moyen |
Oui (web) |
Chiffrement swap |
Moyenne |
Faible |
Oui |
ASLR + PIE |
Élevée |
Nul |
Oui, toujours |
Cellule 1 — Simulation scoring CIS Benchmark avec radar chart#
# Checklist CIS Benchmark simulée — 20 contrôles, 5 catégories
controles = [
# (id, nom, catégorie, poids, statut: 0/0.5/1)
("C01", "SSH PermitRootLogin désactivé", "Authentification", 3, 1.0),
("C02", "MFA sur comptes privilégiés", "Authentification", 4, 1.0),
("C03", "Politique mot de passe renforcée", "Authentification", 3, 0.5),
("C04", "Verrouillage après 5 échecs", "Authentification", 2, 1.0),
("C05", "UFW activé, règles restrictives", "Réseau", 4, 1.0),
("C06", "sysctl IPv4 forwarding désactivé", "Réseau", 3, 0.0),
("C07", "Ports inutilisés fermés", "Réseau", 3, 0.5),
("C08", "TCP SYN cookies activés", "Réseau", 2, 1.0),
("C09", "auditd installé et configuré", "Audit", 4, 1.0),
("C10", "Logs centralisés (syslog distant)", "Audit", 3, 0.5),
("C11", "Rotation des logs activée", "Audit", 2, 1.0),
("C12", "Surveillance /etc/passwd et /etc/shadow", "Audit", 3, 0.0),
("C13", "AIDE — baseline créée", "Intégrité", 4, 0.5),
("C14", "Permissions /etc corrects", "Intégrité", 3, 1.0),
("C15", "SUID/SGID non autorisés supprimés", "Intégrité", 3, 0.5),
("C16", "/tmp et /var/tmp noexec,nosuid", "Intégrité", 3, 1.0),
("C17", "Mise à jour automatique sécurité active", "Patchs", 4, 1.0),
("C18", "Délai max patch CRITICAL ≤ 24h respecté", "Patchs", 4, 0.5),
("C19", "Inventaire actifs à jour", "Patchs", 3, 0.5),
("C20", "Scan vulnérabilités mensuel", "Patchs", 3, 1.0),
]
df_cis = pd.DataFrame(controles, columns=["id", "nom", "catégorie", "poids", "statut"])
df_cis["score_contrôle"] = df_cis["poids"] * df_cis["statut"]
df_cis["score_max"] = df_cis["poids"]
# Score par catégorie
df_cat = df_cis.groupby("catégorie").agg(
score=("score_contrôle", "sum"),
max_score=("score_max", "sum")
).reset_index()
df_cat["pct"] = df_cat["score"] / df_cat["max_score"] * 100
score_global = df_cis["score_contrôle"].sum() / df_cis["score_max"].sum() * 100
# --- Radar chart ---
categories = df_cat["catégorie"].tolist()
valeurs = df_cat["pct"].tolist()
N = len(categories)
angles = [n / float(N) * 2 * pi for n in range(N)]
angles += angles[:1]
valeurs_plot = valeurs + valeurs[:1]
fig = plt.figure(figsize=(13, 5))
# Radar
ax1 = fig.add_subplot(121, polar=True)
ax1.set_theta_offset(pi / 2)
ax1.set_theta_direction(-1)
ax1.set_xticks(angles[:-1])
ax1.set_xticklabels(categories, size=10)
ax1.set_ylim(0, 100)
ax1.set_yticks([20, 40, 60, 80, 100])
ax1.set_yticklabels(["20", "40", "60", "80", "100"], size=7, color="gray")
ax1.plot(angles, valeurs_plot, "o-", color="#2196F3", linewidth=2, markersize=6)
ax1.fill(angles, valeurs_plot, alpha=0.2, color="#2196F3")
ax1.set_title(f"Score CIS par catégorie\nScore global : {score_global:.1f}/100",
fontweight="bold", pad=20)
# Barplot détaillé
ax2 = fig.add_subplot(122)
colors = ["#4CAF50" if s >= 75 else "#FF9800" if s >= 50 else "#F44336" for s in df_cat["pct"]]
bars = ax2.barh(df_cat["catégorie"], df_cat["pct"], color=colors, alpha=0.85)
ax2.axvline(x=75, color="gray", linestyle="--", linewidth=1, label="Seuil 75%")
ax2.set_xlim(0, 110)
ax2.set_xlabel("Score (%)")
ax2.set_title("Score CIS par catégorie", fontweight="bold")
for bar, val in zip(bars, df_cat["pct"]):
ax2.text(val + 1, bar.get_y() + bar.get_height() / 2,
f"{val:.0f}%", va="center", fontsize=10)
ax2.legend(fontsize=9)
plt.suptitle("Simulation CIS Benchmark Ubuntu — État de conformité", fontsize=13, fontweight="bold")
plt.subplots_adjust(wspace=0.4)
plt.show()
print(f"Score global CIS Benchmark : {score_global:.1f}/100")
print("\nDétail par catégorie :")
for _, row in df_cat.iterrows():
etat = "OK" if row["pct"] >= 75 else "À améliorer"
print(f" {row['catégorie']:18s} : {row['pct']:.0f}% — {etat}")
Score global CIS Benchmark : 72.2/100
Détail par catégorie :
Audit : 62% — À améliorer
Authentification : 88% — OK
Intégrité : 73% — À améliorer
Patchs : 75% — OK
Réseau : 62% — À améliorer
Cellule 2 — Scatter EPSS vs CVSS : priorisation des CVEs#
# Génération de 200 CVEs synthétiques
np.random.seed(13)
n_cves = 200
cvss = np.random.beta(2.5, 2.0, n_cves) * 10
# EPSS corrélé faiblement avec CVSS + bruit
epss_base = 0.05 + 0.08 * (cvss / 10)
epss = np.clip(epss_base + np.random.beta(0.5, 5, n_cves) * 0.6, 0.001, 0.999)
# Quelques CVEs critiques connus (simulés)
idx_critiques = np.argsort(cvss)[-15:]
epss[idx_critiques[:8]] = np.random.uniform(0.7, 0.98, 8)
epss[idx_critiques[8:]] = np.random.uniform(0.001, 0.02, 7)
df_cve = pd.DataFrame({"cvss": cvss, "epss": epss})
def quadrant(row):
if row["cvss"] >= 7 and row["epss"] >= 0.1:
return "Critique — patch immédiat"
elif row["cvss"] >= 7 and row["epss"] < 0.1:
return "Sévère — patch planifié"
elif row["cvss"] < 7 and row["epss"] >= 0.1:
return "Exploitabilité haute — remonter"
else:
return "Standard — fenêtre normale"
df_cve["quadrant"] = df_cve.apply(quadrant, axis=1)
sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)
palette_q = {
"Critique — patch immédiat": "#F44336",
"Sévère — patch planifié": "#FF9800",
"Exploitabilité haute — remonter": "#9C27B0",
"Standard — fenêtre normale": "#4CAF50",
}
fig, ax = plt.subplots(figsize=(11, 7))
for quad, grp in df_cve.groupby("quadrant"):
ax.scatter(grp["cvss"], grp["epss"], label=f"{quad} (n={len(grp)})",
alpha=0.7, s=55, color=palette_q[quad], edgecolors="none")
ax.axhline(y=0.1, color="gray", linestyle="--", linewidth=1, alpha=0.7)
ax.axvline(x=7.0, color="gray", linestyle="--", linewidth=1, alpha=0.7)
ax.text(8.5, 0.92, "PATCH IMMÉDIAT", fontsize=9, color="#F44336", fontweight="bold", alpha=0.8)
ax.text(1.0, 0.92, "REMONTER EN\nPRIORITÉ", fontsize=9, color="#9C27B0", fontweight="bold", alpha=0.8)
ax.text(8.5, 0.02, "PATCH\nPLANIFIÉ", fontsize=9, color="#FF9800", fontweight="bold", alpha=0.8)
ax.text(1.0, 0.02, "FENÊTRE\nNORMALE", fontsize=9, color="#4CAF50", fontweight="bold", alpha=0.8)
ax.set_xlabel("Score CVSS v3.1 (sévérité potentielle)")
ax.set_ylabel("Score EPSS (probabilité d'exploitation sur 30 jours)")
ax.set_title("Priorisation des CVEs : CVSS × EPSS\n(200 vulnérabilités synthétiques)", fontweight="bold")
ax.legend(loc="upper left", fontsize=9)
ax.set_xlim(0, 10.2)
ax.set_ylim(-0.02, 1.05)
plt.show()
print("Distribution par quadrant :")
for q, n in df_cve["quadrant"].value_counts().items():
print(f" {q:40s} : {n} CVEs ({100*n/n_cves:.0f}%)")
Distribution par quadrant :
Exploitabilité haute — remonter : 97 CVEs (48%)
Standard — fenêtre normale : 53 CVEs (26%)
Critique — patch immédiat : 43 CVEs (22%)
Sévère — patch planifié : 7 CVEs (4%)
Cellule 3 — Heatmap risque résiduel : contrôles × menaces#
# Heatmap d'efficacité des contrôles de hardening contre les menaces
menaces = [
"Brute force SSH",
"Escalade privilèges",
"Persistance malware",
"Exfiltration données",
"Mouvement latéral",
"Exploitation vuln.",
"Injection commande",
"Accès physique"
]
controles_h = [
"MFA + SSH keys",
"SELinux/AppArmor",
"auditd + AIDE",
"Pare-feu + segmentation",
"Least privilege RBAC",
"Patch management",
"noexec /tmp",
"Chiffrement disque"
]
# Matrice d'efficacité du contrôle (0 = inefficace, 3 = très efficace)
efficacite = np.array([
# MFA SEL AUD FW RBAC PATCH NEXEC CRYPT
[3, 1, 1, 2, 1, 0, 0, 0], # Brute force SSH
[1, 3, 2, 0, 3, 1, 1, 0], # Escalade privilèges
[0, 2, 3, 1, 1, 2, 3, 0], # Persistance malware
[1, 1, 2, 3, 2, 0, 0, 2], # Exfiltration données
[2, 1, 2, 3, 3, 1, 0, 0], # Mouvement latéral
[0, 2, 1, 2, 1, 3, 2, 0], # Exploitation vuln.
[0, 3, 1, 1, 2, 1, 3, 0], # Injection commande
[0, 0, 1, 0, 1, 0, 0, 3], # Accès physique
])
df_heat = pd.DataFrame(efficacite, index=menaces, columns=controles_h)
sns.set_theme(style="white", palette="muted", font_scale=1.1)
fig, ax = plt.subplots(figsize=(13, 7))
cmap = sns.color_palette("YlOrRd", as_cmap=True)
sns.heatmap(
df_heat, annot=True, fmt="d", cmap=cmap,
vmin=0, vmax=3, linewidths=0.5, linecolor="white",
ax=ax, cbar_kws={"label": "Efficacité du contrôle (0=nul, 3=élevée)"}
)
ax.set_title("Efficacité des contrôles de hardening par menace", fontsize=13, fontweight="bold")
ax.set_xlabel("Contrôle de sécurité")
ax.set_ylabel("Menace")
ax.set_xticklabels(ax.get_xticklabels(), rotation=30, ha="right")
# Score de couverture global par menace
score_menace = df_heat.sum(axis=1) / (3 * len(controles_h)) * 100
for i, (menace, score) in enumerate(score_menace.items()):
ax.text(len(controles_h) + 0.1, i + 0.5, f"{score:.0f}%",
va="center", fontsize=9, color="#555")
ax.text(len(controles_h) + 0.1, -0.5, "Couv.", fontsize=9, color="#555", fontweight="bold")
plt.subplots_adjust(right=0.88)
plt.show()
print("Couverture des menaces par les contrôles de hardening :")
for menace, score in score_menace.items():
etat = "Bonne" if score >= 50 else "Insuffisante"
print(f" {menace:30s} : {score:.0f}% — {etat}")
Couverture des menaces par les contrôles de hardening :
Brute force SSH : 33% — Insuffisante
Escalade privilèges : 46% — Insuffisante
Persistance malware : 50% — Bonne
Exfiltration données : 46% — Insuffisante
Mouvement latéral : 50% — Bonne
Exploitation vuln. : 46% — Insuffisante
Injection commande : 46% — Insuffisante
Accès physique : 21% — Insuffisante
Résumé#
Ce chapitre a présenté les outils, référentiels et processus du hardening systèmes et de la gestion des vulnérabilités.
Points clés :
CIS Benchmarks : référence industrielle pour la configuration sécurisée ; le Niveau 1 est applicable universellement, le Niveau 2 pour les environnements haute sécurité. Couvrent Linux, Docker, Kubernetes et plus de 100 technologies.
Lynis : outil d’audit open-source produisant un Hardening Index (0-100) ; intégrable en CI/CD pour valider les images systèmes avant déploiement.
OpenSCAP : automatise la vérification de conformité SCAP (XCCDF/OVAL) contre les profils CIS et DISA STIG ; génère des scripts de remédiation automatiques.
Cycle de vulnérabilités : Discovery → Assessment → Prioritization → Remediation → Verification ; le processus est continu et doit être piloté par des SLA mesurables.
EPSS : complément indispensable au CVSS ; une vulnérabilité CVSS 9.0 avec EPSS 0.001 est moins urgente qu’une CVSS 6.5 avec EPSS 0.85. La combinaison des deux évite la paralysie par l’exhaustivité.
SLA patch management : CRITICAL ≤ 24h, HIGH ≤ 7j, MEDIUM ≤ 30j. Le canary patching réduit le risque de régression en production.
Hardening vs usabilité : chaque contrôle a un coût opérationnel — MFA et
/tmp noexecont un rapport bénéfice/coût excellent ; SELinux/AppArmor nécessitent un investissement de tuning mais sont très efficaces contre l’escalade de privilèges.Visualisation de la couverture : la heatmap contrôles × menaces permet d’identifier rapidement les zones de risque résiduel élevé et de prioriser les investissements en sécurité.