--- name: securite-dev description: Réflexe de sécurité systématique et structuré à appliquer avant de générer, modifier ou aider à configurer tout code, tout site, toute API ou toute infrastructure. Couvre la cartographie de la surface d'attaque, l'authentification, les autorisations, la validation des entrées, la logique métier, les uploads, les API, l'infrastructure, les dépendances, les secrets, l'anti-automatisation, les chaînes d'attaque, et les classes de vulnérabilités avancées — le tout du point de vue défensif (prévention, durcissement, détection), jamais offensif. À utiliser pour tout développeur ou vibecodeur qui veut livrer du code fiable sans craindre les failles. --- # Sécurité avant tout, pour tout, en tout, et après tout ## Rôle de ce skill Avant de générer le moindre code, avant d'aider à configurer quoi que ce soit (serveur, base de données, API, formulaire, upload, paiement, authentification…), penser sécurité de façon structurée, comme le ferait une équipe de sécurité défensive (blue team / secure-by-design), jamais comme un attaquant qui chercherait à exploiter. Ce skill n'est **pas** un guide d'attaque. Il ne doit jamais produire de code d'exploitation, de script de scan agressif contre des cibles tierces, ni de preuve de concept offensive. Il sert à **anticiper** les failles pour les empêcher d'exister, et à **expliquer** les risques pour que le développeur (ou le vibecodeur) comprenne pourquoi une protection est nécessaire. Deux règles d'or gouvernent tout le reste : 1. **Sécurité systématique** — chaque brique de code livrée passe par la checklist pertinente ci-dessous avant d'être considérée terminée. 2. **Propreté avant tout** — la sécurité ne justifie jamais du code superflu, des couches inutiles, ou de la "sécurité décorative" qui impressionne sans protéger. Une protection qui n'a pas de menace réelle en face d'elle est du bruit, pas de la sécurité. Chaque contrôle ajouté doit répondre à un risque identifiable dans le contexte réel du projet. ## Méthode : cartographier avant de coder Avant d'écrire une ligne, se poser mentalement ces questions et ajuster le niveau de rigueur à la taille réelle du projet (un site vitrine n'a pas les mêmes enjeux qu'une plateforme de paiement) : - Qu'est-ce que cette fonctionnalité expose comme donnée, comme accès, comme action ? - Qui peut l'atteindre (utilisateur anonyme, utilisateur connecté, admin, autre service) ? - Qu'est-ce qu'un utilisateur malveillant pourrait faire s'il contrôlait chaque paramètre, chaque en-tête, chaque fichier envoyé ? - Qu'est-ce qui se passe si cette requête est rejouée, dupliquée, envoyée dans le désordre, ou envoyée 10 000 fois ? - Est-ce que cette faiblesse, combinée à une autre déjà présente ailleurs dans le projet, ouvrirait une chaîne plus grave ? ## 1. Cartographie de la surface Avant de sécuriser, savoir ce qui existe et ce qui est exposé : domaines/sous-domaines, apps web, API REST/GraphQL, endpoints oubliés ou de test laissés en prod, apps mobiles, fichiers JS exposés (avec sourcemaps), formulaires, sessions, stockage de fichiers, WebSockets, services cloud, CDN, DNS, certificats, versions de frameworks et de dépendances, environnements staging/dev accessibles publiquement, dépôts Git exposés (`.git/` sur un serveur web est une fuite classique et facile à éviter). **Réflexe pratique** : avant de livrer, dresser une liste courte de "ce que ce projet expose publiquement" et vérifier qu'aucun élément de test, de debug ou d'administration n'y traîne par erreur (endpoint `/debug`, `phpinfo()`, panneau d'admin sans authentification, etc.). ## 2. Front-end : ne jamais faire confiance au client Le HTML, le JS, les bundles et leurs sourcemaps sont visibles par tout le monde. Conséquences pratiques : - Ne jamais mettre de secret (clé API privée, clé de service, mot de passe) dans le JS livré au navigateur. Une clé publique (ex. clé publique Stripe) n'est pas un problème en soi ; une clé qui donne un pouvoir d'action (écriture, suppression, envoi d'e-mail en masse) l'est. - Tout contrôle qui compte (prix, quantité, droit d'accès, validation de paiement) doit être **revalidé côté serveur**, jamais seulement côté client. Le JS peut désactiver un bouton ; il ne peut pas empêcher une requête directe vers l'API. - Ne pas laisser de sourcemaps de production accessibles si elles révèlent la logique métier ou des routes internes non documentées. - Vérifier qu'aucune fonctionnalité "cachée" (panneau admin, route non liée dans le menu) ne soit protégée uniquement par le fait qu'elle n'est pas visible dans l'UI. ## 3. Authentification Points à couvrir systématiquement pour toute app avec compte utilisateur : - **Mots de passe** : hachage avec un algorithme lent et salé (bcrypt/argon2 — jamais MD5/SHA1 seul), politique de longueur minimale raisonnable plutôt que des règles de complexité artificielles. - **Sessions** : cookies `HttpOnly`, `Secure`, `SameSite=Lax` ou `Strict` selon le besoin ; expiration réelle côté serveur, pas seulement côté client ; invalidation effective à la déconnexion (le token doit devenir inutilisable, pas juste supprimé du navigateur). - **Récupération de compte** : lien à usage unique, à durée de vie courte, invalidé après utilisation ; ne jamais révéler si un e-mail existe ou non dans un message d'erreur (« si ce compte existe, un e-mail a été envoyé »). - **MFA** si le contexte le justifie (comptes admin, données sensibles) : le contournement de MFA doit être testé (est-ce qu'une étape peut être sautée en rejouant une requête ?). - **OAuth/OIDC** : valider `state` pour éviter le CSRF sur le flux de connexion, vérifier l'audience et l'émetteur du token, ne jamais faire confiance à un rôle envoyé par le client. - Séparer clairement les rôles utilisateur/admin dès la conception de la base de données, pas en rustine plus tard. ## 4. Autorisations — la faille la plus fréquente et la plus grave Le test de base à appliquer à **chaque** endpoint qui manipule une ressource identifiée par un ID : > Utilisateur A peut-il accéder à la ressource de l'utilisateur B en changeant juste l'identifiant dans l'URL ou dans le corps de la requête ? Cela couvre l'IDOR/BOLA (accès direct à un objet non autorisé), l'élévation de privilège horizontale (A accède aux données de B) et verticale (un utilisateur simple accède à des fonctions admin). Réflexe de code : **toujours vérifier la propriété de la ressource côté serveur**, à chaque lecture, écriture et suppression — jamais supposer qu'un ID dans l'URL appartient forcément à l'utilisateur connecté. Ce contrôle doit être une fonction centrale réutilisée, pas recopiée à la main dans chaque route (source d'oubli). ## 5. Validation des entrées Toute donnée qui vient du client est non fiable par défaut : paramètres GET/POST, JSON, en-têtes, cookies, fichiers et leurs noms, URLs, métadonnées, champs de recherche/filtres, paramètres GraphQL, messages WebSocket. Principe défensif : valider en **liste blanche** (ce qui est autorisé) plutôt qu'en liste noire (ce qui est interdit), et **échapper en sortie** selon le contexte (HTML, SQL, shell, URL ne s'échappent pas de la même façon). Familles à neutraliser systématiquement, avec le réflexe correspondant : - **XSS** → échapper à l'affichage selon le contexte HTML/JS/attribut ; utiliser les mécanismes d'échappement du framework plutôt que du code artisanal. - **Injection SQL/NoSQL/LDAP** → requêtes préparées / paramétrées uniquement, jamais de concaténation de chaînes dans une requête. - **Injection de commande** → éviter d'appeler un shell avec une entrée utilisateur ; si c'est inévitable, liste blanche stricte des valeurs possibles. - **Injection de template (SSTI)** → ne jamais passer une entrée utilisateur comme template à interpréter. - **Path traversal** → normaliser le chemin et vérifier qu'il reste dans le dossier autorisé avant tout accès fichier. - **SSRF** → si le serveur doit appeler une URL fournie par l'utilisateur, restreindre à une liste blanche de domaines/IP et bloquer les plages internes (127.0.0.1, 169.254.169.254, réseaux privés). - **Désérialisation dangereuse** → ne jamais désérialiser une structure complexe venant du client sans schéma strict. ## 6. Logique métier — les failles invisibles aux scanners Une application peut n'avoir aucune faille technique classique et rester gravement vulnérable à cause de sa logique métier. Cas à vérifier systématiquement pour tout site marchand ou à comptes : - Le prix, la remise ou la quantité sont-ils recalculés côté serveur, ou seulement transmis depuis le client au moment du paiement ? - Un coupon à usage unique est-il vérifié et marqué comme utilisé de façon atomique (pas de fenêtre de double utilisation) ? - Une limite (nombre d'essais, quota, remboursement) peut-elle être contournée en créant plusieurs comptes ou en rejouant une action dans un ordre différent ? - Une action peut-elle être validée deux fois (double soumission, double clic, requête rejouée) ? ## 7. Uploads de fichiers Vérifier le type réel du contenu (pas seulement l'extension ou le `Content-Type` déclaré, facilement falsifiables), refuser les doubles extensions suspectes, renommer les fichiers stockés plutôt que de garder le nom fourni par l'utilisateur, limiter la taille, stocker si possible hors du dossier servi directement par le serveur web (ou avec des permissions d'exécution désactivées), et ne jamais servir un fichier uploadé avec des droits d'exécution. Pour les images, une reconversion (re-encodage) neutralise la plupart des payloads cachés dans les métadonnées. ## 8. API (REST, GraphQL, WebSockets) - N'exposer que les champs nécessaires en sortie (éviter l'excessive data exposure : renvoyer l'objet complet de la base de données par facilité est une fuite fréquente). - Vérifier l'autorisation à chaque endpoint, y compris les méthodes HTTP inattendues (un `DELETE` ou `PUT` oublié dans les contrôles alors que le `GET` est protégé). - Se protéger du mass assignment : n'accepter en écriture que les champs explicitement autorisés, jamais un objet entier tel quel. - Rate limiting et pagination par défaut sur les endpoints coûteux ou qui listent des données. - Messages d'erreur génériques en production ; les détails techniques (stack trace, requête SQL) ne doivent jamais atteindre le client. - GraphQL en particulier : limiter la profondeur de requête et la complexité pour éviter qu'une requête imbriquée ne surcharge le serveur. ## 9. Infrastructure Quand le périmètre le permet : principe du moindre privilège pour chaque rôle cloud et chaque service (un service qui lit des fichiers n'a pas besoin d'un droit d'écriture global), secrets jamais en clair dans le code ou les variables d'environnement versionnées, bases de données jamais exposées directement à Internet, buckets de stockage objet vérifiés en privé par défaut, CI/CD avec des secrets scellés (pas en clair dans les logs), certificats et renouvellement automatique, monitoring des erreurs et des accès anormaux. Le risque le plus grave n'est souvent pas la faille applicative elle-même, mais la chaîne de privilèges qui en découle (une app compromise avec un rôle cloud trop large donne accès à toute la production). ## 10. Dépendances Maintenir les frameworks et bibliothèques à jour, surveiller les CVE connues sur les versions utilisées, mais toujours vérifier l'impact réel avant de paniquer ou de rassurer à tort : une CVE ne s'applique que si la fonctionnalité concernée est utilisée, dans une configuration qui l'expose, avec un chemin d'accès réellement atteignable. Préférer des dépendances activement maintenues et limiter le nombre de dépendances à ce qui est réellement utile (moins de dépendances = moins de surface). ## 11. Secrets Ne jamais committer de clé API, secret OAuth, token, fichier `.env`, identifiants CI/CD ou certificat dans un dépôt Git, même privé au départ. Utiliser un gestionnaire de secrets ou des variables d'environnement non versionnées. Vérifier l'historique Git avant de rendre un dépôt public (un secret supprimé dans un commit ultérieur reste visible dans l'historique). Faire tourner (régénérer) immédiatement tout secret qui a pu fuiter, même brièvement. ## 12. Anti-automatisation Rate limiting sur les endpoints sensibles (connexion, réinitialisation de mot de passe, envoi d'e-mail, paiement), verrouillage progressif après plusieurs échecs (pas un blocage permanent facile à exploiter pour bloquer un compte tiers), CAPTCHA sur les formulaires publics exposés aux bots, limites explicites sur les endpoints coûteux en ressources (recherche, export, génération de PDF). ## 13. Penser en chaîne, pas en faille isolée Une bonne défense ne se limite pas à corriger des failles une par une ; elle imagine ce qu'un enchaînement de faiblesses modérées peut produire : ``` information publique accessible ↓ compte utilisateur classique ↓ contrôle d'autorisation faible sur un endpoint ↓ accès à une fonction interne ↓ configuration cloud trop permissive ↓ accès à des données sensibles ``` Chaque maillon pris seul peut sembler mineur ; la chaîne complète peut être critique. Avant de livrer un projet, se demander si deux ou trois petites faiblesses tolérées séparément pourraient, mises bout à bout, ouvrir un accès qu'aucune d'elles ne donnerait seule. ## 14. Revue de code source (quand le code est disponible) Suivre le chemin d'une donnée de bout en bout : ``` entrée utilisateur → validation → traitement → base de données → fonction sensible → sortie ``` À chaque étape, vérifier : contrôle d'autorisation manquant, validation incohérente entre deux endpoints qui font la même chose différemment, secret en dur, fonction dangereuse (`eval`, exécution de commande, désérialisation libre), erreur de concurrence sur une ressource partagée, appel système non nécessaire, accès fichier non borné, requête construite par concaténation. ## 15. Classes de vulnérabilités avancées à garder en tête Au-delà des classes classiques : race conditions et TOCTOU (vérifier puis utiliser une ressource sans garantie d'atomicité), request smuggling et désynchronisation HTTP (pertinent surtout derrière un reverse proxy/CDN), cache poisoning et cache deception, prototype pollution (JS), confused deputy, mauvaise séparation de tenants dans une app multi-clients, erreurs cryptographiques (algorithmes obsolètes, mauvaise gestion des IV/nonces), SSRF via une chaîne indirecte (l'app appelle un service interne qui, lui, fait la requête dangereuse). ## 16. Quand rien n'est trouvé Ne jamais conclure « je n'ai rien trouvé donc c'est sécurisé ». Changer d'angle : tester un autre rôle, un autre workflow, la logique métier plutôt que la technique, comparer le comportement observé au comportement attendu par la documentation ou le code. L'absence de preuve n'est pas une preuve d'absence. ## Équilibre à tenir dans chaque livraison - Appliquer la checklist pertinente au **contexte réel** du projet (ne pas imposer du MFA et du rate limiting élaboré à un simple formulaire de contact sans compte utilisateur ; en revanche ne jamais négliger l'autorisation et la validation des entrées, qui s'appliquent presque toujours). - Signaler explicitement les failles potentielles repérées pendant le développement, même si elles ne sont pas demandées, et proposer la correction avec le code. - Ne jamais ajouter de couche de sécurité qui n'a pas de menace réelle en face : la sécurité inutile alourdit le code, complique la maintenance et peut elle-même devenir une source de bugs. - Le code livré reste lisible, commenté simplement, sans superflu : une fonction de sécurité doit être aussi propre que le reste du code, pas un bloc opaque « parce que c'est plus sûr ».