Sécurité¶
Toutes les plateformes de l'écosystème reposent sur un même socle de sécurité. Cette page définit ce socle : lorsque vous ajoutez une plateforme, considérez ces points comme des exigences, non des options.
Défense en profondeur¶
Les couches s'empilent selon le principe qu'aucune ne suffit à elle seule :
| Couche | Protection |
|---|---|
| Edge (nginx) | TLS, HSTS, rate limit, limite de taille du corps |
| Application (HTTP) | En-têtes de sécurité, allow-list CORS, CSRF, timeouts |
| Application (logique) | RBAC, contrôle des droits, validation des entrées |
| Base de données | RLS, requêtes paramétrées |
| Audit | Journal d'audit chaîné par hachage |
Le jeton n'atteint jamais le JS client¶
Le modèle BFF (Backend-for-Frontend) est le fondement de la sécurité côté frontend.
Le navigateur ne s'adresse jamais directement au backend : il ne parle qu'à des routes Next.js du même domaine, qui font office de proxy depuis le serveur. Le jeton vit dans un cookie httpOnly.
Résultat : le XSS cesse d'être un vecteur de vol de jeton. Même en parvenant à exécuter du JavaScript, un attaquant n'atteint pas le jeton.
S'y ajoute une double protection CSRF : en-tête dédié et vérification d'origine.
En-têtes de sécurité¶
Sur chaque réponse :
| En-tête | Objectif |
|---|---|
Content-Security-Policy |
Ferme le vecteur d'exécution du XSS |
Strict-Transport-Security |
Impose HTTPS (HSTS) |
Cross-Origin-Opener-Policy |
Isole des attaques inter-fenêtres |
Cross-Origin-Embedder-Policy |
Contrôle les ressources externes |
Cross-Origin-Resource-Policy |
Restreint l'usage externe des ressources |
Le CORS fonctionne par allow-list — jamais *.
Row-Level Security de Postgres¶
L'isolation des données d'organisations et d'utilisateurs est appliquée au
niveau de la base de données, et non par une clause WHERE dans le code
applicatif.
Pourquoi : un seul WHERE user_id = ? oublié dans le code et les données
fuitent. La RLS applique ce contrôle à toutes les requêtes, indépendamment
des erreurs de codage.
Garde-fou d'applicabilité au démarrage
Au démarrage, l'application vérifie que la RLS est réellement active. Si une policy manque, ou si l'utilisateur de connexion a le droit de contourner la RLS, l'application refuse de démarrer.
Car une RLS silencieusement désactivée est le cas le plus dangereux de tous : tout paraît fonctionner normalement.
SQL¶
- Requêtes paramétrées — pas de concaténation de chaînes.
- Pas d'ORM — du SQL écrit à la main sur
pgx. Chaque requête est visible.
Rate limits et timeouts¶
| Protection | Où |
|---|---|
| Rate limit d'authentification (strict) | Chemins /login, /oauth, /auth, /api/auth |
| Rate limit général de l'application (souple) | Les autres chemins |
| Rate limit d'API | API publiques du type /v1/* |
| Timeouts du serveur HTTP | read · write · idle · header — tous configurés |
| Limite de taille du corps | Au niveau de l'edge |
Les limites strictes uniquement sur l'authentification
Appliquer le rate limit strict de l'authentification à tous les chemins
fait dépasser la limite aux prefetch RSC de Next.js, qui reçoivent alors des
503. La limite stricte ne porte donc que sur les chemins
d'authentification, avec une limite souple ailleurs.
Gestion des secrets¶
- Chiffrement des jetons — les jetons OAuth tiers (Google Drive, Dropbox, etc.) sont stockés chiffrés en AES-256-GCM.
- Clés cryptographiques — dans des HSM ; le HSM sur site et le HSM cloud basculent l'un sur l'autre.
- Secrets applicatifs — par variables d'environnement, jamais versionnés dans git.
Ne mettez pas de secrets dans le dépôt
Identifiants serveur, clés d'API, client secrets, PIN de HSM : rien de tout cela n'est jamais écrit dans un dépôt, un script ou de la documentation. Les scripts de déploiement n'acceptent les secrets que par variables d'environnement.
En cas d'exposition accidentelle : (1) effectuer immédiatement une rotation, (2) examiner les journaux d'accès, (3) migrer le secret vers un système de gestion de secrets.
Endpoints fermés en production¶
/metrics et /swagger exposent la structure interne et la liste des
endpoints. En production, ils sont protégés par un bearer token.
Journal d'audit¶
Un journal chaîné par hachage et en ajout seul. Chaque enregistrement contient le hachage du précédent.
Ce que cela implique : aucun enregistrement intermédiaire ne peut être supprimé ou modifié — la chaîne se rompt et le contrôle d'intégrité échoue. Seuls les administrateurs le lisent, et une opération distincte permet d'en vérifier l'intégrité.
Signature et non-répudiation¶
Les actes aux conséquences juridiques exigent une signature PIN2 — l'authentification (PIN1) ne suffit pas. Voir Authentification et autorisations pour le détail.
La clé privée de signature est scindée par ECDSA à seuil 2-of-2 — aucune des parties ne peut signer seule.
Tests¶
- Tests unitaires — logique métier et contrôles de droits.
- Tests d'intégration testcontainers — avec de vrais PostgreSQL/Redis, qui éprouvent la RLS pour de bon.
- Tests golden vector — compatibilité cryptographique au niveau du protocole.
Liste de contrôle sécurité¶
Avant la mise en production d'une nouvelle plateforme :
- [ ] TLS + HSTS actifs, certificats renouvelés automatiquement
- [ ] Tous les en-têtes de sécurité configurés (CSP · HSTS · COOP · COEP · CORP)
- [ ] Allow-list CORS — pas de
* - [ ] Rate limits : strict sur l'authentification, souple ailleurs
- [ ] Tous les timeouts du serveur HTTP configurés
- [ ] RLS active et garde-fou de démarrage opérationnel
- [ ] Toutes les requêtes paramétrées
- [ ] Jetons en cookies httpOnly, jamais accessibles au JS client
- [ ] Double protection CSRF
- [ ]
/metricset/swaggerfermés - [ ] Journal d'audit alimenté, intégrité vérifiable
- [ ] Secrets par variables d'environnement, absents du dépôt
- [ ] Tests d'intégration éprouvant réellement la RLS