Sécuriser une application web : par où commencer quand on est seul

Sécurité DevSecOps Guide Méthode
Mis à jour le

Sur un projet sans équipe dédiée, la question n’est pas « comment tout sécuriser », elle est « par quoi commencer ». Voici l’ordre que j’utilise, du contrôle qui rapporte le plus à celui qui peut attendre.

1. Les routes sans vérification de propriété

C’est le défaut le plus fréquent et le moins visible. Il ne s’agit pas de l’absence d’authentification, que tout le monde attrape, mais du cas où un utilisateur connecté agit sur la ressource d’un autre parce que l’identifiant vient de l’URL et n’est jamais confronté à la session.

Le recensement tient en une commande :

grep -rLn "session\|getServerSession\|permissions" app/api --include=route.ts

Elle liste les routes où aucun de ces mots n’apparaît. Chacune est à ouvrir.

Un exemple réel : une route accordait une récompense sur un identifiant de joueur fourni par l’appelant. Aucune authentification n’était contournée, le compte appelant était valide. Il n’était simplement jamais comparé à la cible.

2. Les secrets qui partent chez le visiteur

Tout ce qui n’est pas préfixé NEXT_PUBLIC_ et qui atteint un composant client part dans le paquet JavaScript, donc chez tout le monde.

grep -rln "'use client'" app components | xargs grep -ln "SECRET\|TOKEN\|_KEY"

Puis l’historique, qui est le vrai sujet :

git log --all --diff-filter=A --name-only -- '*.env*'

Un secret qui a été poussé une fois est compromis, même supprimé depuis. Il se change, il ne se supprime pas. C’est le point le plus souvent négligé d’un audit, parce que le retirer donne l’impression d’avoir réglé le problème.

3. Les adresses fournies par l’utilisateur et appelées par le serveur

Si votre application va chercher une URL que quelqu’un lui donne, elle peut atteindre votre réseau interne, y compris les métadonnées d’instance de votre hébergeur, qui contiennent souvent des jetons.

Le refus doit intervenir avant la connexion, et porter sur les adresses privées, de bouclage, lien-local, et les noms sans point. Le filtrage après résolution ne suffit pas : un nom peut résoudre vers une adresse publique la première fois et vers une adresse privée la seconde.

4. Le HTML qui vient du navigateur

Du HTML injecté, une redirection dont la cible vient d’un paramètre, une politique de sécurité de contenu qui existe mais contient unsafe-inline.

Ce dernier point mérite une attention particulière : une politique neutralisée est pire que pas de politique, parce qu’elle donne l’illusion inverse. Même chose pour une protection contre la falsification de requête qui possède un mode permissif. Un garde qui a un mode permissif n’est pas un garde.

5. Les données personnelles

Combien de temps gardez-vous les adresses IP, les journaux, les messages ? Une suppression de compte supprime-t-elle vraiment, ou laisse-t-elle des lignes liées ?

Ces questions ne sont pas de la conformité pour la conformité : une base qui garde tout est une base dont la fuite coûte plus cher.

6. Les dépendances et le conteneur

En dernier, volontairement. Le chiffre brut d’un audit de dépendances est une liste de candidats, pas une mesure de risque, et il fait perdre beaucoup de temps à qui le prend au pied de la lettre.

Deux questions valent le détour : le conteneur tourne-t-il en root, et les versions sont-elles épinglées. Une version de cadriciel en fin de support est un risque permanent même sans faille publiée.

Le contenu d’un rapport

Trois sections, et les deux dernières sont celles qu’on oublie.

Les défauts, classés par gravité, chacun avec sa chaîne d’exploitation complète : qui, comment, ce qu’il obtient. Un nom de catégorie ne suffit pas.

Ce qui est bien fait. Sans cette section, quelqu’un défera une protection en croyant simplifier.

Les fausses pistes écartées, avec leur raison. Sinon le prochain audit les retrouve et refait le travail. J’ai vu le même cycle trois fois sur un même projet.

Le facteur humain, hors de cet ordre

Il ne couvre pas le facteur humain, qui est la première cause d’incident réel : comptes partagés, mots de passe réutilisés, accès jamais retirés au départ de quelqu’un. Aucun de ces six contrôles ne l’attrape.

Il ne remplace pas non plus un test d’intrusion mené par quelqu’un d’autre. On trouve mal les défauts de sa propre construction, et je ne fais pas exception.