Skip to Content
Sécurité

07 — Sécurité (avant chaque livraison : /security-pass)

« Je n’ai pas envie qu’un client se fasse hacker son site par ma faute. » Mindset : le 100 % n’existe pas. La question n’est pas « être inviolable » mais réduire la surface d’attaque + limiter le rayon d’explosion (defense in depth). Une vitrine statique est déjà très sûre (pas de serveur/DB) → version allégée. Dès qu’il y a backend / DB / auth / formulaires → version sérieuse.

0. Sécurité du repo (GitHub) — surface souvent négligée

« Privé donc safe » = faux : si un repo privé fuite, c’est tout l’historique qui est exposé.

  • Compte : 2FA obligatoire, méthode forte (passkey/TOTP), jamais SMS (SIM swap). En org, imposer la 2FA au niveau org.
  • Accès = moindre privilège : freelance/client = accès au repo concerné seulement, retiré dès la fin de mission (accès orphelin = faille). Gérer par teams en org.
  • Secrets dans l’historique : activer secret scanning + push protection + gitleaks/trufflehog en local. Si fuite : révoquer + régénérer D’ABORD (c’est ça qui protège), PUIS nettoyer l’historique. L’ordre compte.
  • Branch protection main : bloquer le force-push (intégrité, compatible avec le push-direct solo). Require PR + review = surtout en équipe. ⚠️ Indisponible sur repo privé en plan gratuit (403 « Upgrade to Pro »).
  • Tokens : fine-grained, perms minimales, expiration 30-90 j. Déploiement : deploy keys read-only si suffisant.
  • GitHub Actions : pinner les actions tierces à un SHA, permissions: minimal sur GITHUB_TOKEN, méfiance pull_request_target, jamais logger un secret.
  • Hygiène : .gitignore correct dès l’init. Bonus : signature des commits.

1. Secrets — LE piège n°1 du code généré par IA

  • Aucun secret en dur, jamais (clés API, token Sanity, service-role Supabase, SMTP) → variables d’environnement.
  • .env* dans .gitignore AVANT le premier commit. Puis scanner l’historique — une clé committée puis « supprimée » reste lisible dans le git history.
  • Une clé qui a fui se RÉVOQUE et se régénère, pas juste « retirée du code ».
  • Client vs serveur : sur Next, tout NEXT_PUBLIC_* part dans le bundle = public. Service-role Supabase / token d’écriture Sanity = serveur only, jamais dans un "use client".

2. Données & accès (si backend)

  • Supabase = RLS sur chaque table, policies explicites par opération. + policy RESTRICTIVE require_aal2 si MFA. Sans RLS, l’anon key (publique) donne accès à tout.
  • Autorisation TOUJOURS côté serveur. Un if (user.isAdmin) en React se contourne en 2 s. Vérif serveur-side (server action / RLS), surtout pour les actions en service-role qui bypassent la RLS.
  • Formulaires publics : rate limiting + anti-bot (Turnstile Cloudflare / hCaptcha) contre spam/abus.

3. Le code

  • Validation des entrées côté serveur (form, query, payload) typée + validée, idéalement Zod. Ne jamais faire confiance au navigateur.
  • XSS : attention à dangerouslySetInnerHTML ; pour le rich text (Portable Text Sanity), passer par un sanitizer (DOMPurify) ou un renderer sûr.
  • Injection : dès qu’on écrit du SQL brut / une RPC, paramétrer, jamais concaténer de chaîne user.

4. Transport & headers

  • HTTPS partout (auto sur Cloudflare/Vercel ; sur o2switch, vérifier certif + redirection http→https).
  • Security headers : CSP + HSTS + X-Content-Type-Options: nosniff + Referrer-Policy + Permissions-Policy. Tester sur securityheaders.com + observatory.mozilla.org. (Hors kit auto : application guidée, une CSP mal réglée peut casser le site.)

5. RGPD (clients FR)

  • Minimisation, bandeau de consentement réel si cookies/analytics non essentiels, mentions légales + politique de confidentialité, durée de conservation définie.
  • Hébergement EU : Cloudflare/Vercel régions EU ; Supabase = région EU à la création (Frankfurt/Paris, pas us-east).
  • Analytics par défaut Cloudflare Web Analytics (sans cookie → pas de bandeau requis). → 05.

6. Dans la durée

npm audit régulier (patcher le transitif via overrides, jamais audit fix --force à l’aveugle). Dependabot activé : (a) alerts + security updates, (b) version-updates via .github/dependabot.yml. Surveiller les logs.

Kit de durcissement Songe (déposé auto par /security-pass)

Fichiers communs à tous les repos (autonomes, additifs, idempotents) ; templates dans ~/.claude/templates/ :

  • dependabot.yml.github/ si absent.
  • gitignore-baseline → garantir que .env* est ignoré.
  • Hors kit (jamais auto) : security headers (fusion + CSP délicate) et branch protection (réglage API) → restent en report-only/guidé.

Vérifs EMPIRIQUES (exécuter, pas supposer)

  • Lire chaque table avec la clé anon → 0 ligne attendu.
  • grep du client service-role / token dans les "use client" → aucun ; scan history (gitleaks).
  • npm audit --omit=dev. Note des headers sur securityheaders.com.

Staging non indexable jusqu’à la prod

  • Baseline noindex gated : robots:{index:false,follow:false,…} dans metadata du layout, actif sauf NEXT_PUBLIC_INDEX==="on". Vérif : curl -s URL | grep 'name="robots"'.
  • Mur d’auth = seulement si données réelles ou si demandé « privé/fermé » (un outil interne muré doit AUSSI être noindex, sinon sa page /login reste indexable).
  • Jour de la livraison seulement : NEXT_PUBLIC_INDEX=on + retirer la protection éventuelle + brancher le vrai domaine.
Last updated on