Validra

Exemple de rapport. L'application est fictive ; les constats sont adaptés de missions réelles anonymisées, avec des détails modifiés.

Exemple de rapport

Ce que vous recevez concrètement à la fin d'une revue.

Voici la structure de chaque rapport Validra, remplie avec des constats réalistes. Les preuves sont masquées ici comme elles le seraient avant de partager un rapport hors de votre équipe.

Application
Acme Billing — SaaS B2B de facturation (Next.js + Supabase)
Périmètre convenu
Application web, API REST, base et stockage Supabase, webhooks Stripe
Accès utilisés
Dépôt en lecture seule, environnement de staging, deux comptes de test
Durée de la revue
5 jours ouvrés, puis un retest après correction
Auditeur
Téo Brondel, Validra

1. Résumé exécutif

1

Critique

2

Élevée

1

Moyenne

1

Faible

La revue a identifié un problème critique : n'importe quel utilisateur connecté pouvait lire et modifier les transactions des autres clients en appelant directement l'API de la base de données. Deux problèmes de gravité élevée exposaient des jetons de session dans les logs serveur et des documents internes dans un stockage public.

Le problème critique et celui des jetons ont été corrigés pendant la mission et confirmés au retest. Le problème de stockage est partiellement corrigé : le bucket est désormais privé, mais les fichiers déjà publics doivent être considérés comme exposés.

Priorité pour l'équipe : terminer la correction du stockage, puis limiter les tentatives de connexion et de réinitialisation de mot de passe.

Comment les résultats sont classés

Gravité — l'impact pour votre activité

  • CritiqueAccès direct aux données ou à l'argent d'autres utilisateurs, exploitable maintenant.
  • ÉlevéeExposition sérieuse, mais qui demande une condition (un log qui fuit, une URL devinée).
  • MoyenneRend une attaque plus facile ou plus probable ; à corriger rapidement.
  • FaibleDurcissement ; à corriger quand c'est possible.

Certitude — à quel point c'est prouvé

  • Démontrée — Reproduit sur votre environnement, avec preuve.
  • Faiblesse — Une vraie faiblesse d'une protection, sans exploitation complète démontrée.
  • Observation — Bon à savoir, pas une vulnérabilité en soi.

2. Vue d'ensemble des résultats

IDRésultatGravitéCertitudeStatut après retest
F-01N'importe quel utilisateur connecté peut lire et modifier les transactions des autres clientsCritiqueDémontréeCorrigé
F-02Jetons de session écrits en clair dans les logs serveurÉlevéeDémontréeCorrigé
F-03Documents internes téléchargeables sans authentificationÉlevéeDémontréePartiellement corrigé
F-04Aucune limite de tentatives sur la connexion et la réinitialisation du mot de passeMoyenneFaiblesseOuvert
F-05En-tête Content-Security-Policy absentFaibleObservationOuvert
F-01CritiqueDémontrée

N'importe quel utilisateur connecté peut lire et modifier les transactions des autres clients

Surface : Contrôle d'accès à la base (Row-Level Security Supabase)

Ce que nous avons trouvé

La RLS était activée sur la table transactions, mais ses politiques SELECT et UPDATE vérifiaient seulement que l'utilisateur était connecté, pas que la ligne lui appartenait. L'interface filtrait correctement, ce qui masquait le problème ; un appel direct à l'API REST renvoyait les lignes de tous les clients.

Preuve — requête faite avec le compte de test B (masquée)

curl "https://[projet].supabase.co/rest/v1/transactions?select=*" \
  -H "apikey: [clé anon]" \
  -H "Authorization: Bearer [session du compte B]"

# 200 OK — 1 248 lignes, dont celles du compte A
# et de 37 autres organisations

Impact pour l'activité

Les données financières de tous les clients étaient lisibles, et modifiables, par n'importe quel utilisateur disposant d'un compte, y compris un compte d'essai gratuit.

Recommandation

Restreindre les deux politiques aux lignes de l'organisation de l'utilisateur, et ajouter un test qui échoue si un autre compte peut lire une ligne.

create policy "org members read own transactions"
on transactions for select
using (org_id in (
  select org_id from memberships where user_id = auth.uid()
));

Retest

Corrigé. La même requête avec le compte B ne renvoie plus que les lignes de son organisation.

F-02ÉlevéeDémontrée

Jetons de session écrits en clair dans les logs serveur

Surface : Authentification et sessions

Ce que nous avons trouvé

Le lien d'export de facture passait le jeton de session (JWT) de l'utilisateur en paramètre d'URL. Les URL complètes, jeton compris, étaient conservées dans les logs de l'hébergeur et dans l'historique du navigateur.

Preuve — ligne de log du staging (masquée)

GET /export?invoice=[id]&token=eyJhbGciOi…[masqué] 200

Impact pour l'activité

Toute personne ayant accès aux logs, ou à l'historique d'un ordinateur partagé, pouvait réutiliser une session valide et agir à la place de l'utilisateur jusqu'à l'expiration du jeton.

Recommandation

Transmettre le jeton dans l'en-tête Authorization (ou s'appuyer sur le cookie de session httpOnly), raccourcir la durée de vie des jetons et purger les logs existants qui en contiennent.

Retest

Corrigé. L'export utilise désormais le cookie de session ; aucun jeton n'apparaît dans les URL ni dans les nouveaux logs.

F-03ÉlevéeDémontrée

Documents internes téléchargeables sans authentification

Surface : Permissions de stockage

Ce que nous avons trouvé

Les contrats signés déposés par les clients étaient stockés dans un bucket public, sous des chemins prévisibles basés sur le numéro de facture.

Preuve — requête sans authentification (masquée)

curl -I "https://[projet].supabase.co/storage/v1/object/public/contracts/INV-2026-0142.pdf"

# HTTP/2 200 — ni session, ni jeton

Impact pour l'activité

Des contrats clients confidentiels étaient accessibles à toute personne qui devinait ou trouvait une URL.

Recommandation

Rendre le bucket privé, servir les fichiers via des URL signées de courte durée vérifiées selon l'organisation de l'utilisateur, et renommer les fichiers existants pour invalider les anciennes URL.

Retest

Partiellement corrigé. Le bucket est privé et les nouveaux liens sont signés. Les fichiers existants gardent leur ancien nom : les URL déjà partagées doivent être considérées comme exposées.

F-04MoyenneFaiblesse

Aucune limite de tentatives sur la connexion et la réinitialisation du mot de passe

Surface : Authentification

Ce que nous avons trouvé

Les endpoints de connexion et de réinitialisation ont accepté 50 tentatives consécutives depuis la même IP, sans délai ni blocage. Aucun compte n'a été compromis pendant le test.

Observation pendant les tests

50 échecs de connexion en 40 secondes depuis une IP → pas de limite, pas de blocage, pas d'alerte

Impact pour l'activité

Rend réaliste la devinette de mots de passe et l'envoi massif d'e-mails de réinitialisation.

Recommandation

Ajouter une limite par IP et par compte sur les deux endpoints, avec un délai progressif court.

Retest

Ouvert. Prévu par l'équipe au prochain sprint.

F-05FaibleObservation

En-tête Content-Security-Policy absent

Surface : Configuration

Ce que nous avons trouvé

L'application n'envoie pas d'en-tête Content-Security-Policy. Aucun point d'injection n'a été trouvé pendant la revue.

Observation

En-têtes de réponse : pas de content-security-policy

Impact pour l'activité

Pas une vulnérabilité en soi, mais limiterait les dégâts d'une future injection de script.

Recommandation

Ajouter une CSP, d'abord en mode report-only pour ne rien casser.

Retest

Ouvert. Priorité faible.

3. Périmètre et limites

Testé

  • Row-Level Security sur toutes les tables exposées par l'API
  • Autorisations des routes API et server actions, avec deux comptes de test
  • Connexion, sessions et réinitialisation du mot de passe
  • Secrets dans le dépôt et dans le bundle JavaScript livré
  • Permissions des buckets de stockage
  • Vérification de signature des webhooks Stripe

Non testé

  • Infrastructure d'hébergement et réseau
  • Déni de service et tenue en charge
  • Applications mobiles et services tiers au-delà de leurs points d'intégration
  • Tout ce qui sort du périmètre convenu — une revue ne rend compte que de ce qui a été testé

4. Synthèse de mission partageable

Un bloc court et non technique, à coller dans un questionnaire de sécurité client.

Acme Billing a fait l'objet d'une revue de sécurité externe par Validra (Téo Brondel), indépendante de l'équipe Acme.

Périmètre : application web, API, contrôle d'accès à la base, stockage, authentification, webhooks Stripe. Méthode alignée sur le Top 10 OWASP, avec tests manuels entre deux comptes de test.

Résultat : 5 constats (1 critique, 2 élevés, 1 moyen, 1 faible). Les problèmes critiques et élevés ont été corrigés ou atténués et vérifiés au retest.

Cette synthèse décrit une revue réalisée à une date donnée, dans un périmètre convenu. Ce n'est pas une certification (type SOC 2 ou ISO 27001) et elle ne garantit pas l'absence d'autres vulnérabilités.

Vous voulez ce rapport pour votre application ?

395 € pour démarrer, environ 5 jours ouvrés. 395 € de plus seulement si la revue identifie des problèmes de sécurité pertinents : 790 € au maximum.