Gestion des secrets¶
API keys, tokens, certificats et mots de passe — stocker, rotater et détecter les fuites avant qu'il ne soit trop tard.
La règle absolue
Un secret commite dans un dépôt Git est compromis. Même supprimé dans un commit suivant, il reste dans l'historique. Même un dépôt prive peut devenir public. Revoquer immédiatement tout secret expose.
Types de secrets¶
| Type | Exemples | Risque en cas de fuite |
|---|---|---|
| Credentials DB | postgres://user:pass@host/db | Accès complet aux données |
| API keys | OpenAI, Stripe, Twilio, AWS | Facturation, accès données, actions métier |
| Tokens OAuth | Access token, refresh token | Usurpation d'identité |
| Clés de chiffrement | Fernet key, AES key, RSA private key | Dechiffrement de toutes les données |
| Certificats TLS | Clé privee .key | Interception du trafic chiffre |
| Secrets CI/CD | Deploy keys, registry credentials | Accès à l'infrastructure |
Détection de fuites : gitleaks et trufflehog¶
gitleaks¶
Analyse l'historique Git complet à la recherche de patterns de secrets.
# Installation
brew install gitleaks
# Scanner le depot courant (tous les commits)
gitleaks detect --source . --verbose
# Scanner uniquement les fichiers staged (pre-commit hook)
gitleaks protect --staged
# Integrer dans la CI (GitHub Actions)
# .github/workflows/security.yml
# .github/workflows/gitleaks.yml
name: Gitleaks
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # historique complet
- uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
trufflehog¶
Specialise dans la détection de secrets avec entropie et vérification live.
# Scanner un depot GitHub
trufflehog github --repo https://github.com/org/repo
# Scanner localement avec verification des secrets actifs
trufflehog git file://. --only-verified
# Scanner une image Docker
trufflehog docker --image python:3.11
Pre-commit hook
Installer gitleaks comme hook pre-commit empêche les commits accidentels :
Variables d'environnement : bonnes pratiques¶
Fichier .env en développement¶
# .env (jamais commite)
DATABASE_URL=postgres://user:pass@localhost/mydb
OPENAI_API_KEY=sk-proj-...
JWT_SECRET=un-secret-long-et-aleatoire
# .env.example (commite — template sans valeurs)
DATABASE_URL=postgres://user:pass@localhost/mydb
OPENAI_API_KEY=
JWT_SECRET=
Charger les variables en Python¶
# VULNERABLE — hard-coded dans le code source
API_KEY = "sk-proj-abc123..."
# SECURISE — depuis l'environnement
import os
from dotenv import load_dotenv
load_dotenv() # charge .env en dev, ignoré en prod si non present
API_KEY = os.environ["OPENAI_API_KEY"] # leve une exception si absent
# ou avec valeur par defaut
DB_HOST = os.environ.get("DB_HOST", "localhost")
HashiCorp Vault¶
Solution de référence pour la gestion centralisee des secrets en production.
flowchart LR
App["Application"] -->|"auth (AppRole/k8s)"| Vault["HashiCorp Vault"]
Vault -->|"token temporaire"| App
App -->|"lire secret"| Vault
Vault -->|"secret + TTL"| App
Vault --> Audit["Audit log"]
Admin["Operateur"] -->|"ecrire/rotater secrets"| Vault Exemple d'intégration Python¶
import hvac
def get_db_credentials():
client = hvac.Client(url="https://vault.internal:8200")
# Authentification via AppRole (recommande pour les applications)
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="database/myapp",
mount_point="secret"
)
return secret["data"]["data"] # {"username": "...", "password": "..."}
Gestionnaires de secrets cloud¶
| Service | Provider | Cas d'usage principal |
|---|---|---|
| AWS Secrets Manager | AWS | Rotation automatique RDS, rotation Lambda |
| GCP Secret Manager | Intégration Cloud Run, GKE Workload Identity | |
| Azure Key Vault | Azure | Certificats TLS, secrets app, clés de chiffrement |
| Doppler | SaaS | Sync multi-environnements, simple à intégrer |
GCP Secret Manager — exemple¶
from google.cloud import secretmanager
def get_secret(project_id: str, secret_id: str, version: str = "latest") -> str:
client = secretmanager.SecretManagerServiceClient()
name = f"projects/{project_id}/secrets/{secret_id}/versions/{version}"
response = client.access_secret_version(request={"name": name})
return response.payload.data.decode("utf-8")
# Utilisation
db_password = get_secret("my-project", "db-password")
Stratégies de rotation¶
La rotation limite la fenêtre d'exposition en cas de fuite non détectée.
sequenceDiagram
participant Scheduler
participant SecretsManager
participant Database
participant App
Scheduler->>SecretsManager: Declencher rotation
SecretsManager->>Database: Creer nouveau mot de passe
SecretsManager->>SecretsManager: Stocker nouveau secret (version N+1)
App->>SecretsManager: Lire secret (version latest)
SecretsManager-->>App: Retourner version N+1
SecretsManager->>Database: Revoquer ancien mot de passe Bonnes pratiques de rotation¶
- Rotation automatique toutes les 30 a 90 jours pour les secrets long-terme
- Tokens d'accès avec TTL court (1h a 24h) et renouvellement automatique
- Rotation immédiate après chaque départ d'un membre de l'équipe
- Alertes si un secret n'a pas été utilisé depuis sa rotation (potentiellement oublie)
Rotation sans downtime
Pour une rotation sans interruption : créer la nouvelle credential, mettre à jour les applications, valider, puis revoquer l'ancienne. Certains secrets managers (AWS) gèrent ce processus automatiquement.
Checklist secrets¶
-
.envdans.gitignoreet jamais commite -
.env.exampleavec toutes les variables (valeurs vides) commite - gitleaks ou trufflehog configuré en pre-commit hook
- Scan de secrets dans la CI sur chaque PR
- Secrets de production dans un gestionnaire de secrets (pas dans les variables CI en clair)
- Rotation automatique activee pour les secrets critiques
- Audit log des accès aux secrets active
Chapitre suivant : Supply chain — sécuriser vos dépendances et votre chaîne de build.