Créer la page Sécurité du wiki

2026-09-08 18:04:51 +02:00
parent 5eaa1c61a0
commit 016104c235

109
S%C3%A9curit%C3%A9.md Normal file

@@ -0,0 +1,109 @@
# Sécurité
## Principes
Le projet applique trois règles absolues pour protéger les secrets :
- **Aucun secret en clair** : ni dans le code source, ni dans les logs, ni dans les messages d'erreur, ni dans les fixtures de test.
- **Masquage systématique** : utiliser `redact_url()`, `redact_secrets()` et `redact_exception()` depuis `utils/redaction.py`.
- **Chaînage d'exceptions** : ne jamais conserver une exception externe brute comme `__cause__` ou `__context__`. Journaliser la version expurgée puis utiliser `raise ... from None`, ou chaîner une cause elle-même expurgée.
## Masquage des données sensibles
### Masquage des URLs
La fonction `redact_url()` masque les paramètres sensibles dans les URLs. Les clés sensibles (insensibles à la casse) sont : `icalsecurise`, `token`, `key`, `password`, `secret`. Elle utilise `urlsplit`/`urlunsplit`/`parse_qsl` pour reconstruire l'URL. En cas d'erreur de parsing, elle retourne `REDACTED_URL`.
### Masquage des secrets dans le texte
La fonction `redact_secrets()` masque les URLs et les tokens isolés dans un texte. Elle traite les motifs comme : `icalsecurise=XXX`, `token=XXX`, `password=XXX`, `secret=XXX`. Les secrets supplémentaires (`extra_secrets`) sont triés par longueur décroissante pour éviter les masquages partiels.
### Masquage des exceptions
La fonction `redact_exception(exc, extra_secrets=())` applique `redact_secrets` au message de l'exception et aux secrets configurés. La méthode `Settings.redaction_secrets()` retourne un tuple de tous les secrets configurés (mots de passe, clés API) à transmettre.
## Configuration des logs
Le *formatter* `RedactingFormatter` dans `utils/logging.py` masque les secrets dans tous les enregistrements de log :
- Masquage de `record.msg`
- Masquage de `record.args` (gère les tuples et les dictionnaires)
- `setup_logging()` configure un *handler* `stdout` avec ce *formatter*
- Les loggers tiers (`urllib3`, `slixmpp`) sont positionnés sur `WARNING`
## Types sécurisés
Les mots de passe et clés API utilisent `pydantic.SecretStr` :
- Ils ne sont jamais sérialisés en clair.
- Ils sont masqués dans les messages d'erreur Pydantic (`hide_input_in_errors` pour les paramètres XMPP).
## Vérification avant déploiement
### Règles de détection
Le script `scripts/check_secrets.py` analyse le dépôt pour détecter les secrets littéraux avec trois motifs regex :
- **Affectation littérale** : `key = "value"` ou `key: "value"` (valeur de 3+ caractères entre guillemets)
- **Affectation non quotée** : pour les fichiers de configuration (`.conf`, `.ini`, `.toml`, `.yaml`, `.yml`), `key = value` (valeur de 3+ caractères non quotée)
- **Paramètre d'URL** : `?token=XXX` ou `&api_key=XXX` (valeur de 3+ caractères)
### Exclusions
Le script exclut :
- Les répertoires `.git`, `.venv`, `.worktrees`, `__pycache__`
- Les fichiers `.env` (jamais versionnés)
- Le fichier `GUIDE_DEV_PYTHON.md` (exemples intentionnels)
- Le répertoire `tests/`
- Les lignes marquées avec `secret-check: allow` (liste blanche locale pour les fixtures/exemples)
### Utilisation
```bash
# Vérifier tout le dépôt
python scripts/check_secrets.py
# Vérifier uniquement les fichiers indexés (avant un commit)
python scripts/check_secrets.py --staged
```
Les codes de sortie sont :
- `0` : aucun secret détecté
- `1` : secrets trouvés
- `2` : erreur lors de l'exécution
Les résultats n'exposent **jamais** la valeur détectée, seulement le chemin, le numéro de ligne et le nom de la règle.
## Tests de non-fuite
La suite de tests inclut des **tests de non-fuite obligatoires** :
- Injecter des valeurs sentinelles distinctes dans l'URL, les identifiants, le mot de passe et la clé API
- Provoquer une erreur externe
- Vérifier l'absence dans :
- Le message d'erreur
- Les logs
- `__cause__` et `__context__`
- Le *traceback* complet
Chaque secret utilise une sentinelle distincte pour identifier précisément toute fuite éventuelle.
## Fichier `.env`
Le fichier `.env` n'est **jamais** commité (couvert par `.gitignore`). Utiliser `.env.example` comme modèle. En production, le fichier doit être local et lisible uniquement par le compte de service :
```bash
sudo install -d -m 0700 -o <service-user> -g <service-group> <config-dir>
sudo install -m 0600 -o <service-user> -g <service-group> .env <env-file>
```
## Bonnes pratiques
- Ne jamais placer de secrets dans les unités systemd, les commandes shell ou les journaux système.
- Éviter de partager les exports de logs sans revue : les données sensibles de l'environnement ou des outils tiers ne doivent pas être supposées sûres par défaut.
- Après modification des identifiants, relancer `check_secrets.py` et un `--dry-run` avant de redémarrer le service.
- En cas d'échec, ne pas redémarrer automatiquement après modification des identifiants : corriger la configuration, relancer les vérifications, puis consulter le journal expurgé.
Voir aussi : [Configuration](Configuration) pour les variables d'environnement, [Déploiement](Déploiement) pour la configuration en production.