Retour au cours

cyber / cybersecurite-fondamentale

Gestion des secrets et vault

Leçon 161 exercice

Explication

Ce que vous allez apprendre

  • Expliquer pourquoi un secret committé dans git reste exposé même après suppression du fichier
  • Sortir les secrets du code vers des variables d'environnement chargées via un fichier .env non versionné
  • Comprendre la différence entre un secret statique et un secret dynamique généré par un vault
  • Intégrer un scanner comme gitleaks en CI pour bloquer un secret avant qu'il n'atteigne la branche principale
  • Réagir correctement à une fuite de secret : révocation immédiate, jamais une simple suppression

Dans quel contexte ?

Un développeur commit par erreur un fichier .env contenant la clé API de production Stripe dans un dépôt GitHub, initialement privé puis rendu public quelques mois plus tard lors d'une réorganisation. Même si le fichier est supprimé du dernier commit, la clé reste parfaitement lisible dans l'historique git, accessible à quiconque clone le dépôt. Seule la révocation immédiate de la clé chez Stripe, pas la suppression du fichier, referme réellement la faille.

Le secret le plus mal gardé : celui dans le code source

Un "secret" désigne toute information sensible qui donne un accès (mot de passe de base de données, clé d'API, clé de chiffrement). Le réflexe naturel d'un débutant est de l'écrire directement dans le code — mais le code finit presque toujours dans un dépôt Git, partagé, historisé, parfois public. Une fois committé, un secret reste visible dans l'historique même après suppression du fichier, sauf réécriture complète de l'historique.

Pourquoi les variables d'environnement sont une première étape, pas la solution finale

Sortir les secrets du code vers des variables d'environnement évite déjà l'erreur la plus commune. Mais ces variables restent statiques, souvent partagées entre développeurs par des canaux peu sûrs (chat, fichier .env envoyé par email), et ne changent jamais automatiquement même après un départ d'employé.

Le saut conceptuel vers un vault

Un vault (coffre-fort de secrets, type HashiCorp Vault) va plus loin : il peut générer des identifiants DYNAMIQUES, valables seulement quelques minutes ou heures, créés à la demande pour chaque service qui en a besoin.

ApprocheDurée de vie du secretExposition en cas de fuite
Codé en dur dans le codeIndéfinieMaximale, visible dans tout l'historique git
Variable d'environnement (.env)Statique, changée manuellementRéduite mais toujours durable
Secret dynamique (vault)Quelques minutes à quelques heuresMinimale, expire de lui-même

C'est un changement de philosophie : au lieu de protéger un secret statique, on réduit la fenêtre de temps pendant laquelle un secret volé reste exploitable.

Le rôle de gitleaks et de la détection automatique en CI

Même avec les meilleures intentions, une erreur humaine peut committer un secret par accident. Un scanner comme gitleaks intégré à la chaîne d'intégration continue (CI) bloque ce commit avant qu'il n'atteigne la branche principale — une protection automatique qui ne dépend pas de la vigilance de chaque développeur.

Le réflexe indispensable en cas de fuite

Piège fréquent

Supprimer un secret d'un fichier ne suffit jamais : tant qu'il a été exposé une seule fois, il doit être considéré comme compromis et RÉVOQUÉ (remplacé par un nouveau), pas simplement caché. La réécriture de l'historique git ne referme pas la faille si le dépôt a déjà pu être cloné ou indexé.

Commandes & code

Gestion des secrets et vault

Un secret (clé API, mot de passe DB, clé de chiffrement) ne doit jamais être codé en dur dans le code source ni versionné dans git.

python
# VULNÉRABLE : secret codé en dur dans le code, versionné dans git pour toujours (même après suppression)
DATABASE_URL = "postgresql://admin:S3cr3tP@ssw0rd@db.technologik.local:5432/prod"  # NE JAMAIS FAIRE ÇA
STRIPE_API_KEY = "sk_live_51H8x..."
python
# CORRECT : chargement depuis des variables d'environnement, injectées par l'environnement d'exécution
import os
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    database_url: str
    stripe_api_key: str
    jwt_secret: str

    class Config:
        env_file = ".env"   # .env DOIT être dans .gitignore, jamais commité

settings = Settings()  # lève une erreur explicite si une variable requise est absente
bash
# .gitignore - s'assurer qu'aucun secret ne peut être commité par accident
cat <<'EOF' >> .gitignore
.env
.env.*
!.env.example
*.pem
*.key
secrets/
EOF

# .env.example versionné : documente les variables attendues SANS valeurs réelles
cat <<'EOF' > .env.example
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
STRIPE_API_KEY=sk_test_xxx
JWT_SECRET=change_me
EOF
bash
# Détecter des secrets déjà commités par erreur avec gitleaks (scan de l'historique complet)
gitleaks detect --source . --verbose --report-path gitleaks-report.json

# Si un secret a fuité dans l'historique git : il faut le RÉVOQUER immédiatement (pas juste le supprimer du repo)
# La réécriture d'historique (git filter-repo / BFG) ne suffit pas seule si le repo a déjà été cloné/publié
text
# HashiCorp Vault : coffre-fort de secrets centralisé, avec bail (lease) et rotation automatique
1. L'application s'authentifie auprès de Vault (AppRole, Kubernetes auth, etc.) - PAS de secret statique
2. Vault génère un secret dynamique à durée de vie limitée (ex: identifiants DB temporaires)
3. Le secret expire automatiquement (lease) -> réduit drastiquement la fenêtre d'exploitation en cas de fuite
4. Toute lecture de secret est journalisée (audit log) -> traçabilité complète
bash
# Exemple d'utilisation de Vault en CLI (environnement de lab)
vault kv put secret/technologik/database url="postgresql://..." password="..."
vault kv get secret/technologik/database

# Secrets dynamiques pour PostgreSQL : Vault crée un utilisateur DB temporaire à la demande
vault write database/roles/technologik-app   db_name=postgresql   creation_statements="CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';   GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO "{{name}}";"   default_ttl="1h" max_ttl="24h"

vault read database/creds/technologik-app   # génère des identifiants valides 1h seulement
python
# Récupération d'un secret Vault au démarrage de l'application (au lieu d'une variable d'env statique)
import hvac

client = hvac.Client(url="https://vault.technologik.local:8200")
client.auth.approle.login(role_id=os.environ["VAULT_ROLE_ID"], secret_id=os.environ["VAULT_SECRET_ID"])

secret = client.secrets.kv.v2.read_secret_version(path="technologik/database")
database_password = secret["data"]["data"]["password"]  # jamais loggé, jamais persisté sur disque en clair
text
# Rotation des secrets : politique recommandée
Clés API tierces (Stripe, SendGrid...) : rotation tous les 90 jours ou immédiatement en cas de suspicion
Mots de passe DB / secrets dynamiques   : via Vault, TTL courts (1h à 24h), rotation automatique
Clés de chiffrement long terme          : rotation annuelle avec ré-chiffrement progressif (voir leçon crypto symétrique)
Clés SSH                                : rotation à chaque changement d'équipe, désactivation immédiate au départ

Résumé

  • Aucun secret en dur dans le code ni dans git : variables d'environnement au minimum, vault au mieux.
  • gitleaks en CI détecte les fuites avant qu'elles n'atteignent la branche principale.
  • Un secret qui a fuité doit être révoqué, pas seulement supprimé de l'historique.
  • Vault permet des secrets dynamiques à durée de vie limitée, réduisant l'impact d'une fuite.

Exercices pratiques

1 disponible
1

Mission : la clé Stripe retrouvée dans l'historique git

Objectif : Réagir correctement à une fuite de secret déjà commitée et distinguer les mesures qui referment vraiment la faille de celles qui sont insuffisantes.

Contexte

Un développeur de Technologik découvre, six mois après coup, qu'un ancien commit sur la branche main contient un fichier .env avec STRIPE_API_KEY=sk_live_51H8x... en clair. Le fichier a depuis été supprimé du code actuel, et le dépôt était privé à l'époque du commit, mais est devenu public il y a deux mois lors d'une réorganisation d'équipe.

Résoudre l’exercice →