Secure App Scaffolder
Génère une application sécurisée dès la conception, conforme aux normes ANSSI, CIS, FBI et OWASP ASVS niveau 2 : frontend TypeScript, backend Python, base de données PostgreSQL durcie.
La mise en page, le README et l'interface utilisateur suivent la compétence project-blueprint. Les règles de sécurité de cette compétence prévalent sur toute règle de commodité ou d'expérience utilisateur.
Fichiers de référence (à lire lors de la génération de l'artefact correspondant)
- reference/docker-compose.yml : Compose durci avec amorçage des secrets.
- reference/nginx.conf : Proxy inverse avec TLS 1.3, PQ hybride, en-têtes, limite de débit.
- reference/security-tests.md : Matrice de tests de sécurité obligatoire.
Quand l'utiliser
- Pour démarrer, structurer ou architecturer une nouvelle application ou service web.
- Pour des requêtes mentionnant ANSSI, CIS, FBI, OWASP, durcissement, rootless, conformité.
- Pour ajouter l'authentification, l'autorisation, les téléchargements de fichiers, la journalisation d'audit, les fonctionnalités d'administration, les fichiers de déploiement ou les points de terminaison MCP.
Cibles de déploiement : Docker et Podman rootless uniquement. Ne générez pas de manifestes Kubernetes, Helm ou autres orchestrateurs, sauf demande explicite de l'utilisateur.
0. Modèle de Menaces
- Créez
docs/security/threat-model.md(≤ 1 page) : actifs, limites de confiance, acteurs, risques principaux (STRIDE) et le contrôle qui les atténue. Mettez-le à jour lorsqu'une nouvelle limite de confiance apparaît (téléchargement, webhook, LLM, MCP).
1. Conteneurs et Exécution
- Rootless : chaque image se termine par un
USERnumérique (par ex.USER 10001:10001), y compris le serveur web (nginxinc/nginx-unprivileged, port > 1024). - Images minimales et épinglées : constructions multi-étapes ; l'étape d'exécution est distroless ou slim, sans shell, compilateur ni gestionnaire de paquets ; dépendances de développement exclues. Images de base épinglées par digest. Dernière version stable de Python.
- Durcissement :
read_only,tmpfspour les chemins accessibles en écriture,cap_drop: [ALL],no-new-privileges, profil seccomp par défaut de l'exécution (jamaisunconfined),pids_limit, limites mémoire, healthchecks, vidages mémoire désactivés (ulimits: core: 0). Seul le proxy inverse publie des ports ; le réseau de la base de données estinternal: true. Ne montez jamais le socket Docker/Podman. - Pas de sous-processus : pas de
subprocess,os.system,shell=True. Applicable par : pas de shell dans l'image, une règle SAST, et un filtre seccomp in-process refusantexecve/execveatchargé dans chaque worker après le démarrage (un refus au niveau du conteneur empêcherait le point d'entrée lui-même de démarrer). - Garantie de premier lancement : sur un clone propre,
docker compose up(ainsi quedocker-compose upetpodman compose up) fonctionne du premier coup : pas de.env, pas d'étape manuelle, aucun prérequis autre que le moteur de conteneur. Les images se construisent automatiquement (build:), seules les fonctionnalités de la spécification Compose prises en charge par Docker Compose v2 et podman-compose sont utilisées, aucun chemin hôte en dehors du dépôt, ports publiés > 1024, pas de limites CPU (indisponibles dans de nombreuses configurations rootless cgroup).make upest une mince surcouche autour de la même commande. - Pas de secrets en dur : Un service init unique génère les secrets dans un volume nommé (
0600) consommé via les variables*_FILE. Les secrets sont lus depuis des fichiers, pas depuis des variables d'environnement, en production. - Autonome (air-gapped) : tous les JS/CSS/polices/icônes sont regroupés ; pas de CDN, de téléchargement à l'exécution ni de télémétrie. Les dépendances proviennent d'un miroir interne (Nexus, Artifactory, devpi, Verdaccio) lors de la construction hors ligne.
- Prêt pour Vault : une seule abstraction de fournisseur de secrets (
filepar défaut,vault/sopsen production). - TLS interne : backend ↔ base de données via TLS ; mTLS optionnel entre les services.
2. Chaîne d'Approvisionnement et Analyse
- Dépendances verrouillées : engagez les lockfiles ;
npm ci,uv sync --frozenoupip install --require-hashes..npmrcavecignore-scripts=truesauf si un paquet nécessite des scripts d'installation (liste d'autorisation). - Passerelle de pré-construction :
npm audit --audit-level=highetpip-auditfont échouer la construction si le code de sortie est > 0. - SAST et secrets : pre-commit avec
ruff,bandit/semgrep,eslint(+eslint-plugin-security),gitleaks. - Analyses d'images (
make scan) :trivy image(CVE + secrets + mauvaises configurations),trivy configsur les Dockerfiles et compose,oscap-podman(ouoscap-docker) contre le profil CIS du système d'exploitation de base. - SBOM et signature (
make sbom) : SBOM CycloneDX (Trivy ou Syft), images signées aveccosign(basé sur une clé pour le hors ligne), SBOM attaché comme attestation ; le déploiement vérifie la signature. - DAST (optionnel, recommandé) : OWASP ZAP baseline contre la pile en cours d'exécution dans l'IC.
- Mises à jour : Renovate/Dependabot (ou une revue hors ligne équivalente) à une cadence fixe.
3. Frontend (TypeScript)
- Emplacement :
/frontendavec son propreDockerfile, construit en tant qu'actifs statiques servis par Nginx non privilégié. - Code compatible CSP : pas de
<script>en ligne, pas deeval/new Function, pas de CSS-in-JS à l'exécution injectant<style>sans nonce. Préférez le CSS compilé. Trusted Types activé (require-trusted-types-for 'script'). - Entrée zéro-confiance : schéma Zod typé par formulaire, partagé avec les types API. Jamais
dangerouslySetInnerHTML/v-htmlsur des données non fiables ; DOMPurify si le texte enrichi est inévitable. - Idempotence : le client API attache une clé d'idempotence (
Idempotency-Key, UUIDv4) à chaque POST/PUT/PATCH/DELETE, générée une fois par action utilisateur et réutilisée en cas de nouvelles tentatives. - CSRF : en-tête personnalisé (
X-Requested-With) + jeton CSRF ; le backend vérifie égalementOrigin/Sec-Fetch-Site. Liste d'autorisation CORS uniquement, jamais*avec identifiants. - Redirections ouvertes :
next/returnUrlaprès connexion n'accepte que les chemins relatifs commençant par un seul/. - Pas de secrets dans le bundle ; pas de source maps en production ; pas de données sensibles dans
localStorage/sessionStorage.
4. Backend (Python) et Base de Données
- Validation : chaque corps, paramètre de requête, paramètre de chemin et en-tête validé par Pydantic avec des longueurs maximales et
extra="forbid"(empêche l'attribution en masse :role,owner_id,is_adminne sont jamais liables depuis l'entrée). - Limites de ressources : taille maximale du corps de requête, profondeur/longueur maximale du tableau JSON, taille maximale de page (par ex. 100), timeouts côté serveur. Les expressions régulières sur les entrées utilisateur sont bornées ou utilisent un moteur à temps linéaire (
re2) pour éviter ReDoS. - Analyse sécurisée : pas de
pickle,marshal,yaml.load(utilisezyaml.safe_load),eval. XML viadefusedxml. Pas d'entrée utilisateur dans les sources de templates (SSTI). Les chemins de fichiers sont résolus et vérifiés par rapport à un répertoire de base (traversée de chemin). - Stockage d'idempotence :
(user_id, key) -> request_hash, status, response, TTL de 24 h. Même clé + même hash renvoie la réponse stockée ; hash différent renvoie422. Cela empêche les doublons ; la protection contre la relecture provient de TLS, des jetons CSRF et des sessions courtes. - SQL : ORM ou requêtes paramétrées uniquement ; identifiants dynamiques (
ORDER BY, noms de colonnes) via une liste d'autorisation. - Chiffrement au niveau des colonnes : les PII sont chiffrées dans l'application avec AES-256-GCM, chiffrement en enveloppe (DEK par enregistrement enveloppé par une clé maîtresse du fournisseur de secrets), colonne
key_versionpour la rotation, index aveugle HMAC-SHA256 pour les champs interrogeables. La clé maîtresse n'est jamais stockée dans la base de données ni dans les sauvegardes de la base de données. - Durcissement de la base de données : PostgreSQL minimal,
scram-sha-256, TLS, deux rôles (propriétaire des migrations, DML d'exécution uniquement), pas de superutilisateur depuis l'application, migrations Alembic,statement_timeoutsur le rôle d'exécution. - Console SQL (si présente) : rôle en lecture seule,
SELECTunique,statement_timeout, limite de lignes, configuration verrouillée. - Cryptographie et post-quantique : AES-256-GCM au repos est résistant au quantique. La préparation PQ cible les échanges de clés et les signatures : hybride TLS
X25519MLKEM768sur le proxy ; ML-KEM/ML-DSA (FIPS 203/204) uniquement là où la bibliothèque les prend en charge. Valeurs aléatoires provenant desecrets/crypto.getRandomValues. N'implémentez jamais de primitives cryptographiques. - Requêtes sortantes (SSRF) : toute URL configurable dans l'interface utilisateur (webhooks, syslog, LLM, OIDC) est validée :
httpsuniquement (sauf liste d'autorisation explicite), IP résolue rejetée si bouclage/lien-local/métadonnées (169.254.0.0/16,fd00:ec2::254) ou privée sauf si autorisée, revérifiée après résolution DNS (rebinding DNS), pas de redirections inter-hôtes, timeouts stricts. - Webhooks sortants : signés avec HMAC-SHA256 (
X-Signature+ timestamp) afin que les récepteurs puissent les vérifier. - E-mail : les valeurs d'en-tête sont dépouillées de CR/LF ; SMTPS avec vérification du certificat.
- Exportations : les cellules CSV/XLSX commençant par
=,+,-,@, tabulation ou CR sont préfixées par'(injection de formule). - Limitation de débit : connexion, réinitialisation de mot de passe, vérification MFA, clés API, exportations, téléchargements et points de terminaison coûteux.
- Réponses :
Cache-Control: no-storesur les réponses API authentifiées. Messages d'erreur génériques avec unrequest_id; traces de pile uniquement dans les logs. Interface utilisateur OpenAPI désactivée ou réservée à l'administrateur en production.
5. Autorisation
- Refus par défaut : chaque route déclare son autorisation requise ; une route sans déclaration fait échouer le démarrage ou les tests.
- RBAC avec des rôles mappés aux autorisations (pas de vérification directe des rôles dans le code).
- Vérifications au niveau des objets (IDOR/BOLA) : vérification de propriété ou de locataire dans la couche de dépôt pour chaque lecture/écriture par ID. L'accès non autorisé à un objet existant renvoie
404. - Vérifications au niveau des fonctions : les routes d'administration sous
/api/v1/admin/*sont protégées par une seule dépendance, jamais uniquement en masquant l'interface utilisateur. - Actions sensibles (changements de rôle, création de clés, exportations, suppression d'utilisateurs) nécessitent une ré-authentification récente ; approbation optionnelle à quatre yeux pour l'élévation de rôle en administrateur.
6. Téléchargements de Fichiers (le cas échéant)
- Limite de taille au niveau du proxy et de l'application ; liste d'autorisation des extensions et vérification des magic bytes.
- Stockés en dehors de la racine web (ou stockage d'objets), noms de fichiers aléatoires, servis avec
Content-Disposition: attachmentetX-Content-Type-Options: nosniff. - Les images sont ré-encodées (supprime les EXIF et les polyglots) ; les archives sont vérifiées pour les bombes et la traversée de chemin ; antivirus hors ligne optionnel (ClamAV).
- Les téléchargements SVG et HTML sont refusés sauf s'ils sont nettoyés et servis depuis une origine isolée.
7. Journalisation, Audit et Administration
- Logs structurés : JSON, niveaux DEBUG/INFO/WARN/ERROR/CRITICAL,
request_idpartout, timestamps UTC, CR/LF supprimés des valeurs utilisateur. Les mots de passe, jetons, clés et ID de session ne sont jamais journalisés. - Syslog distant : RFC 5424 sur TLS (RFC 5425) avec un tampon local limité ; hôte, port, TLS, CA configurables depuis l'interface d'administration. Pas de conteneur rsyslog.
- Notifications d'erreur : SMTPS et webhooks configurables depuis l'interface d'administration, validés contre le SSRF, bouton "Envoyer un test", limités en débit.
- Masquage des PII : middleware activable depuis l'interface d'administration.
- Piste d'audit inviolable : table en ajout seul (le rôle d'exécution a
INSERT/SELECTuniquement), chaque ligne chaînée avec le hash de la ligne précédente, transférée à syslog. Couvre les connexions, échecs, modifications MFA, changements de rôle, modifications de configuration, exportations, clés API, hits honeytoken ; enregistre l'utilisateur, l'IP, le user agent, l'heure UTC et les valeurs avant/après. - Tableau de bord d'administration : statistiques d'utilisation, erreurs, alertes honeytoken, IP bloquées.
- Hygiène planifiée : désactiver les comptes inactifs, purger les jetons/sessions/clés d'idempotence expirés, sauvegardes chiffrées quotidiennes (clé séparée de la sauvegarde), politique de conservation et procédure de restauration testée (
make restore-test). - Données personnelles (RGPD) : périodes de conservation par type de données, exportation et suppression des données utilisateur, tâches de purge.
8. Authentification, Identité et Défense Active
- Mots de passe : Argon2id (valeurs par défaut argon2-cffi / RFC 9106, minimum m=19 MiB, t=2, p=1), re-hachage à la connexion lorsque les paramètres changent. Politique selon NIST SP 800-63B : ≥ 12 caractères, max ≥ 64, pas de règles de composition, rejeté s'il figure dans une liste groupée de mots de passe compromis/courants.
- Connexion : réponse et timing identiques pour les utilisateurs inconnus, délais progressifs + limitation par compte et par IP (éviter le verrouillage dur qui permet un déni de service).
- Réinitialisation de mot de passe : jeton unique à usage unique stocké haché, expiration 30 min, même réponse que le compte existe ou non, toutes les sessions révoquées après la réinitialisation.
- Sessions : côté serveur, cookie
__Host-session(HttpOnly; Secure; SameSite=Lax; Path=/), ID tourné à la connexion et au changement de privilège, les utilisateurs peuvent lister et révoquer leurs sessions actives. Pas de JWT dans le stockage du navigateur ; si des JWT sont utilisés service à service : algorithme épinglé,nonerefusé,aud/iss/expvérifiés, durée de vie courte. - Sessions administrateur : 1 h d'inactivité, 8 h absolues, ré-authentification pour les actions destructrices.
- MFA : TOTP (codes de récupération hachés, rejeu du même code refusé) et WebAuthn/Passkeys. MFA obligatoire pour les comptes administrateurs.
- Détection de contexte : les connexions administrateur depuis une IP/périphérique inconnu déclenchent une notification "Nouvelle connexion" et restreignent les droits destructeurs jusqu'à confirmation MFA.
- Administrateur d'amorçage : mot de passe aléatoire généré au premier démarrage, lisible une fois via
make admin-password, réinitialisation forcée + inscription MFA à la première connexion. - Compte de secours (optionnel) : administrateur local utilisable lorsque le SSO est indisponible, désactivé par défaut, chaque utilisation déclenche une alerte critique.
- SSO : flux OIDC code + PKCE,
stateetnoncevalidés, signature/aud/issdu jeton d'ID vérifiée ; LDAPS ou StartTLS avec vérification du certificat, valeurs des filtres LDAP échappées (injection LDAP). Groupes d'annuaire mappés aux rôles. - Configuration SSO, deux sources équivalentes : chaque paramètre OIDC et LDAP(S) (indicateur d'activation, URL du serveur/émetteur, ID client, portées, DN de liaison, DN de base, filtres utilisateur/groupe, bundle CA, mappage groupe-à-rôle, auto-provisioning) peut être défini soit depuis l'interface d'administration soit depuis des variables d'environnement (
BACKEND_OIDC_*,BACKEND_LDAP_*).- Les secrets (
client_secret, mot de passe de liaison) proviennent des variables*_FILEou du fournisseur de secrets, jamais de valeurs d'environnement en clair en production ; dans l'interface utilisateur, ils sont en écriture seule (••••last4). - Précédence : un paramètre défini par l'environnement l'emporte et est affiché en lecture seule dans l'interface d'administration avec un badge "Géré par l'environnement" ; tout le reste est modifiable et stocké chiffré dans la base de données.
- Même validation pour les deux sources (schéma Pydantic partagé par le chargeur d'environnement et l'API d'administration) ; une configuration d'environnement invalide fait échouer le démarrage avec une erreur claire, une entrée invalide dans l'interface utilisateur est rejetée avant sauvegarde.
- L'interface d'administration a une action "Tester la connexion" (découverte OIDC + récupération JWKS, liaison LDAP + recherche d'exemple de groupe) et chaque modification est auditée.
- Les secrets (