Exemple de rapport
À quoi ressemble un rapport de Code Audit, du début à la fin.
Un rapport complet pour une application fictive, pour que vous en voyiez la structure et le niveau de détail avant d’en commander un. L’application est inventée. Les données de vulnérabilités proviennent d’une exécution réelle de composer audit.
Exemple de rapport, application fictive
« Acme Fleet Portal » et Acme Logistics n’existent pas. Le code, les métriques et les constats de configuration de ce rapport sont inventés pour montrer la structure et le niveau de détail d’un Code Audit Refberg. Les données de vulnérabilités sont réelles : le 24 octobre 2026, nous avons exécuté composer audit sur un fichier lock contenant les versions de paquets listées en section 4.1. Le nombre d’avis, les identifiants CVE et les versions corrigées proviennent de cette exécution. Les prix sont des exemples fondés sur nos prix de départ publics.
Rapport de Code Audit
| Client | Acme Logistics Ltd (fictif) |
|---|---|
| Application | Acme Fleet Portal : réservations, facturation et API JSON pour l’application mobile des chauffeurs |
| Taille de l’audit | M (de 50 000 à 200 000 lignes de code PHP) |
| Accès | dépôt en lecture seule, .env de préproduction et de production (expurgés), fichiers de configuration du serveur, crontab |
| Date | 24 octobre 2026 |
| Rédigé par | Refberg |
1. Synthèse pour la direction
Le portail fonctionne et rapporte de l’argent, mais il repose sur des logiciels qui ont cessé de recevoir des correctifs de sécurité entre 2022 et 2025. Trois problèmes demandent une action cette semaine, avant tout travail de montée de version :
- Le serveur de préproduction peut être pris en main depuis Internet. Il est accessible publiquement, tourne en mode debug et contient un composant touché par une faille publiée d’exécution de code à distance (CVE-2021-3129). La préproduction contient une copie hebdomadaire de la base de production, avec les noms, adresses et factures des clients.
- Les mots de passe de production et la clé de chiffrement de l’application sont dans l’historique Git. Toute personne qui possède, ou a possédé, une copie du dépôt peut les lire. Ils n’ont pas été changés depuis 2021.
- Les sauvegardes sont stockées sur le même serveur que la base de données et n’ont jamais été restaurées. Une panne de disque ou un rançongiciel sur ce serveur emporterait les données et les sauvegardes en même temps.
Ces trois points peuvent être réglés en un à deux jours, sans modifier le code de l’application (section 6, étape 0).
Le problème de fond, c’est la plateforme elle-même : PHP 7.4 (fin de vie le 28 novembre 2022) et Laravel 6 (fin des correctifs de sécurité le 6 septembre 2022). L’analyse des dépendances a relevé 114 avis de sécurité publiés dans 20 paquets. Certains se corrigent avec des versions correctives au sein de Laravel 6. La plupart exigent la montée de version.
Il n’y a presque pas de tests automatisés (6,1 % de couverture de lignes, aucun sur les six parcours critiques pour l’activité) : aujourd’hui, toute modification se fait à l’aveugle. Nous recommandons l’option A : construire d’abord un filet de sécurité, puis monter de version en deux étapes au forfait jusqu’à Laravel 13 sous PHP 8.4, sur environ quatre mois, avec Care Standard à partir du 5e mois. L’option B (rester sur Laravel 6 avec un PHP corrigé) coûte moins cher cette année, et la section 6 explique dans quels cas elle se justifie.
2. Périmètre et méthode
Dans le périmètre : le code de l’application dans le dépôt (branche main au commit 4f1c9e2), composer.json et composer.lock, l’historique Git, les fichiers de configuration, la configuration serveur fournie par le client, le script de déploiement.
Outils : composer audit (Composer 2, base d’avis de sécurité de Packagist), PHPStan 2.x sans l’extension Laravel (Larastan pour Laravel 6 n’existe que pour l’ancienne branche PHPStan 0.12 ; nous relançons donc l’analyse avec Larastan après la montée de version du framework), Rector 2.x en mode dry-run, PHP_CodeSniffer avec PHPCompatibility, PHPUnit avec PCOV pour la couverture, gitleaks pour les secrets dans l’historique, git log pour la fréquence des modifications. Puis une revue manuelle des routes, de l’authentification, des envois de fichiers, de la génération de PDF, des files de messages et des tâches planifiées.
Hors périmètre : tests d’intrusion, tests de charge, revue de l’application mobile, systèmes du prestataire de paiement, configuration réseau de l’hébergeur. Nous n’avons tenté d’exploiter aucune faille. Quand nous disons qu’une vulnérabilité est exploitable, cela signifie que ses conditions préalables publiées sont réunies dans votre configuration.
3. Carte des risques
Probabilité et impact sont notés de 1 (faible) à 3 (élevé). Priorité : P0 cette semaine, P1 pendant la première étape, P2 pendant la montée de version.
| ID | Risque | Probabilité | Impact | Priorité |
|---|---|---|---|---|
| R1 | Exécution de code à distance sur la préproduction publique via Ignition (CVE-2021-3129) ; la préproduction contient des données personnelles de production | 3 | 3 | P0 |
| R2 | APP_KEY, mots de passe de la base et de la messagerie exposés dans l’historique Git depuis 2021 | 3 | 3 | P0 |
| R3 | Sauvegardes sur le même serveur, jamais restaurées | 2 | 3 | P0 |
| R4 | Génération de PDF avec ressources distantes activées sur dompdf 0.8.6 (CVE-2022-28368, injection de code à distance via les polices) | 2 | 3 | P0 |
| R5 | Aucun test sur les parcours critiques ; 9 des 42 tests existants échouent | 3 | 2 | P1 |
| R6 | PHP 7.4 d’Ubuntu 20.04 sans Ubuntu Pro : aucune mise à jour de sécurité de l’environnement d’exécution depuis mai 2025 | 2 | 3 | P1 |
| R7 | Laravel 6 en fin de vie ; 5 avis sur le framework n’ont pas de correctif dans la branche 6.x | 2 | 2 | P2 |
| R8 | Paquets abandonnés ou remplacés (SwiftMailer, Faker, Ignition 1.x) | 2 | 2 | P2 |
| R9 | Un ancien développeur détenait toute la connaissance ; pas de runbook, déploiement par un script shell lancé à la main | 3 | 2 | P1 |
4.1 Dépendances
Résultat : 114 avis dans 20 paquets : 13 critiques, 44 élevés, 50 moyens, 7 faibles. 110 d’entre eux concernent des dépendances de production. Une sélection de paquets :
| Paquet | Installé | Avis | Avis notables | Première version corrigée | Corrigeable au sein de Laravel 6 ? |
|---|---|---|---|---|---|
laravel/framework | 6.20.0 | 11 | CVE-2021-21263 (liaison des paramètres de requête), CVE-2021-43808 (XSS dans Blade), CVE-2024-52301 (environnement modifiable via la query string) | 6.20.45 pour 6 des 11 | en partie : 6.20.45 |
facade/ignition (dev) | 1.16.3 | 3 | CVE-2021-3129 (exécution de code à distance) | 1.16.14 (1.16.15 pour les trois) | oui ; et il ne doit pas être installé en production |
dompdf/dompdf | 0.8.6 | 14 | CVE-2022-28368 (injection de code à distance via les polices), CVE-2021-3838 (désérialisation) | 1.2.1 / 2.0.x / 3.1.6 | en partie : exige une montée de version majeure de laravel-dompdf |
guzzlehttp/guzzle | 6.5.5 | 14 | CVE-2022-31090, CVE-2022-31091 (identifiants conservés après une redirection vers une autre origine) | 6.5.8 pour 5 des 14 | en partie |
firebase/php-jwt | 5.2.0 | 2 | CVE-2021-46743 (confusion clé/algorithme) | 6.0.0 | oui, avec des modifications de code (nouvelle API) |
league/flysystem | 1.0.70 | 2 | CVE-2021-32708 (situation de concurrence lors de l’écriture de fichiers) | 1.1.4 | oui |
phpoffice/phpspreadsheet | 1.15.0 | 29 | XXE dans le lecteur XLSX (CVE-2024-45293, CVE-2024-48917) | 1.29.4 et suivantes (pour les deux failles XXE) | probablement, via une version plus récente de maatwebsite/excel 3.1.x ; à confirmer à l’étape 1 |
swiftmailer/swiftmailer | 6.2.3 | 1 | CVE-2024-28859 (gadget de désérialisation) | 6.2.5 | oui, mais le paquet est abandonné |
Abandonnés ou remplacés : swiftmailer/swiftmailer est marqué comme abandonné sur Packagist au profit de symfony/mailer (adopté par Laravel 9). fzaninotto/faker est marqué comme abandonné. facade/ignition a été remplacé par spatie/laravel-ignition ; sa dernière version date de janvier 2023. fideloper/proxy n’a connu aucune version depuis février 2022 ; ses fonctionnalités ont été intégrées à Laravel 9.
Pourquoi c’est plus important que le décompte : les éléments dangereux sont ceux dont votre configuration remplit les conditions préalables. Dans cette application, il s’agit d’Ignition en préproduction (R1, le mode debug est activé), de dompdf avec enable_remote => true dans config/dompdf.php (R4, le modèle de facture charge le logo par URL), et de php-jwt dans l’API mobile (nous n’avons pas pu confirmer l’exploitabilité sans connaître la configuration des clés ; voir 4.4).
4.2 Tests et CI
- 42 tests PHPUnit, dont 9 échouent sur
main. Les échecs datent de 2023, et les tests n’ont plus été exécutés depuis. - Couverture de lignes : 6,1 %. Sont couverts : deux classes utilitaires et le formulaire de connexion.
- Parcours critiques sans aucun test : création de réservation, calcul du prix, génération des factures (PDF), callback de paiement, authentification de l’API chauffeurs, export mensuel des factures (XLSX).
- Aucun pipeline de CI.
deploy.shlancegit pull,composer install(sans--no-dev, d’où la présence d’Ignition et de Faker sur les serveurs) etphp artisan migrate --forceen production, dans cet ordre, sans étape de sauvegarde.
4.3 Analyse statique et préparation à la montée de version
| Contrôle | Résultat |
|---|---|
| PHPStan niveau 0 (sans extension Laravel) | 286 erreurs. 41 sont des appels à des méthodes ou fonctions non définies, dont au moins 7 sur des chemins de code accessibles depuis les routes |
| PHPStan niveau 5 (sans extension Laravel) | 1 912 erreurs. Nous recommandons une baseline au niveau 5, et aucune nouvelle erreur à partir de l’étape 1 |
| PHPCompatibility, cible PHP 8.4 | 37 erreurs, 112 avertissements |
| Rector en dry-run, jeu de règles PHP 8.1 | 214 fichiers seraient modifiés ; environ 80 % des changements sont mécaniques (promotion des propriétés du constructeur, str_contains, opérateur nullsafe) |
Points bloquants pour PHP 8, dans l’ordre où ils apparaîtraient :
app/Libraries/LegacyCsv.phputiliseeach(), supprimée en PHP 8.0. Cette classe sert à l’import mensuel : erreur fatale dès la première exécution après la montée de version.- 23 classes utilisent des propriétés dynamiques (dépréciées en PHP 8.2), surtout dans
app/Services/Pricing/. - Interpolation de chaînes
${…}(signe dollar hors des accolades) à 31 endroits (dépréciée en PHP 8.2). - Paramètres implicitement nullables dans 58 signatures de méthodes (dépréciés en PHP 8.4).
- Laravel 6 est pris en charge au plus sur PHP 8.0 : les montées de version de PHP et du framework doivent donc être imbriquées (section 6).
Points chauds
Les fichiers les plus souvent modifiés au cours des 24 derniers mois, avec leur taille.
| Fichier | Lignes | Commits (24 mois) | Remarque |
|---|---|---|---|
app/Http/Controllers/BookingController.php | 2 140 | 61 | tarification, validation et e-mails dans une seule classe ; premier candidat pour les tests de caractérisation |
app/Services/InvoiceService.php | 1 310 | 38 | génération des PDF, numérotation, TVA ; la numérotation utilise MAX(id)+1 sans verrou |
app/Http/Controllers/Api/DriverController.php | 870 | 29 | gestion des JWT écrite à la main autour de php-jwt |
4.4 Configuration et sécurité
| ID | Constat | Preuve | Recommandation |
|---|---|---|---|
| C1 | La préproduction est publique, APP_DEBUG=true, Ignition installé | staging.acme-fleet.example répond sans authentification | restreindre par IP ou par authentification HTTP dès aujourd’hui ; passer APP_DEBUG=false ; déployer avec --no-dev |
| C2 | La base de préproduction est une copie hebdomadaire de la production, avec des données personnelles | tâche cron refresh-staging.sh | remplacer par une copie anonymisée ; c’est aussi un problème RGPD |
| C3 | Secrets dans l’historique | gitleaks : .env.production versionné en mars 2021 puis supprimé dans un commit ultérieur ; contient APP_KEY, DB_PASSWORD, MAIL_PASSWORD | renouveler les trois ; voir la remarque sur APP_KEY ci-dessous |
| C4 | Proxies de confiance réglés sur * | app/Http/Middleware/TrustProxies.php | indiquer l’adresse du répartiteur de charge ; aujourd’hui, n’importe quel client peut définir sa propre IP via X-Forwarded-For, ce qui neutralise la limitation des tentatives de connexion |
| C5 | Cookie de session sans attribut Secure, pas de HSTS | SESSION_SECURE_COOKIE non défini ; pas d’en-tête Strict-Transport-Security | activer les deux après avoir vérifié que tout passe en HTTPS |
| C6 | Ressources distantes de dompdf activées | config/dompdf.php : enable_remote => true | intégrer le logo comme fichier local et désactiver les ressources distantes |
| C7 | Worker de file lancé par cron toutes les minutes, en root | crontab -l de root | le faire tourner sous Supervisor ou systemd, avec l’utilisateur de l’application |
| C8 | Sauvegardes : mysqldump chaque nuit vers /var/backups sur le même serveur | crontab, contenu du disque | copie hors site, chiffrement, test de restauration mensuel |
| C9 | La vérification des JWT accepte l’algorithme indiqué dans l’en-tête du jeton | DriverController::decodeToken() | traité par la montée de version vers php-jwt 6 ; corriger le gestionnaire en même temps |
À propos du renouvellement d’APP_KEY
Laravel utilise cette clé pour les cookies chiffrés, les URL signées et toute valeur que votre code chiffre avec Crypt::encrypt() ou encrypt(). La changer déconnecte tous les utilisateurs et rend ces valeurs illisibles si elles ne sont pas chiffrées à nouveau. Le portail stocke un champ de ce type (les numéros d’identité nationaux des chauffeurs, chiffrés dans un mutator du modèle). L’étape 0 comprend un court script qui déchiffre avec l’ancienne clé et rechiffre avec la nouvelle.
4.5 Environnement d’exécution
Le serveur de production tourne sous Ubuntu 20.04 avec le paquet PHP 7.4 de la distribution. La maintenance de sécurité standard d’Ubuntu 20.04 a pris fin en mai 2025. Le serveur n’est pas rattaché à Ubuntu Pro : le paquet PHP et le reste du système d’exploitation ne reçoivent donc plus aucune mise à jour de sécurité.
Rattacher le serveur à Ubuntu Pro rétablirait ces mises à jour jusqu’en mai 2030 pour les paquets du dépôt main d’Ubuntu, dont fait partie PHP 7.4. Cela ne corrige que l’environnement d’exécution, et ne change rien pour Laravel ni pour les paquets de la section 4.1.
4.6 Ce qui est en bon état
- Les migrations reconstruisent le schéma complet à partir d’une base vide, sans erreur : un environnement reproductible ne demande aucun SQL manuel.
- Le callback de paiement vérifie la signature du prestataire avant de modifier une commande.
- L’application utilise les files, le planificateur et les composants mail de Laravel, sans remplacements maison : la montée de version peut suivre les guides officiels.
5. Priorités
| Priorité | Quoi | Pourquoi en premier |
|---|---|---|
| P0, cette semaine | C1, C2, C3, C6, C8 | exploitable dès maintenant, ou rendrait un incident irrécupérable |
| P0, cette semaine | laravel/framework 6.20.0 → 6.20.45, league/flysystem → 1.1.4, guzzlehttp/guzzle → 6.5.8, guzzlehttp/psr7 → 1.9.1 | versions correctives, même version majeure, risque faible même sans tests |
| P1, étape 1 | tests sur les six parcours critiques, CI, runbook, supervision, C4, C5, C7, Ubuntu Pro ou un nouveau serveur | rien d’autre ne peut être modifié sans risque tant que ce socle n’existe pas |
| P2, étapes 2 et 3 | montée de version de PHP et de Laravel, remplacement des paquets abandonnés, C9 | exige le filet de tests de l’étape 1 |
6. Plan et prix fermes
Les prix de cet exemple sont fondés sur nos prix de départ publics. Un vrai audit se termine par un devis comme celui-ci, avec des prix fermes pour le périmètre qui y est décrit.
Option A (recommandée) : filet de sécurité, puis Laravel 13 sous PHP 8.4
| Étape | Périmètre | Durée | Prix ferme | Paiement |
|---|---|---|---|---|
| 0. Contenir | les points classés P0 en section 5 ; votre équipe peut s’en charger en 1 à 2 jours en suivant nos instructions pas à pas, ou nous le faisons pendant la première semaine de l’étape 1 | 1 semaine | inclus dans l’étape 1 | avec l’étape 1 |
| 1. Takeover | environnement Docker qui reproduit la production ; CI ; tests de caractérisation pour les six parcours critiques ; baseline PHPStan au niveau 5 ; sauvegardes hors site avec test de restauration ; supervision ; runbook et cartographie de l’architecture ; inventaire des accès | 4 semaines (taille M) | 3 000 € | 100 % à la commande |
| 2. Montée de version I | Laravel 6 → 9 et PHP 7.4 → 8.1 par paliers (6 → 8 sous PHP 7.4, puis PHP 8.0, puis Laravel 9 sous PHP 8.1) ; SwiftMailer → Symfony Mailer ; php-jwt 6 avec la correction du gestionnaire JWT ; dompdf 2.x ; Guzzle 7 ; Faker → fakerphp/faker ; réécriture de LegacyCsv | 6 semaines | 6 000 € | 30 % / 70 % après une CI au vert et votre validation en préproduction |
| 3. Montée de version II | Laravel 9 → 13 et PHP 8.1 → 8.4 ; serveur Ubuntu 24.04 ; phpspreadsheet et dompdf dans leurs versions actuelles ; suppression de fideloper/proxy ; propriétés dynamiques et dépréciations traitées | 6 semaines | 6 000 € | 30 % / 70 % après une CI au vert et votre validation en préproduction |
| Care Standard | mises à jour mensuelles de PHP et des dépendances, supervision, 8 heures d’intervention, prise en charge des incidents critiques sous 4 heures ouvrées, revue trimestrielle | à partir du 5e mois | 800 €/mois | mensuel |
Option B : maintenir en sécurité sur la version actuelle pendant 12 mois
Étapes 0 et 1 comme ci-dessus (3 000 €), Ubuntu Pro sur le serveur existant pour les correctifs de l’environnement d’exécution (abonnement payé par vous à Canonical), les versions correctives de la section 5, et Care Basic à 400 € par mois.
Cette option garde les failles évidentes fermées pendant que vous décidez de l’avenir de l’application. Elle ne supprime ni les 5 avis Laravel sans correctif dans la branche 6.x, ni les problèmes de dompdf et de phpspreadsheet qui exigent des montées de version majeures, ni les points bloquants pour PHP 8. Dans 12 mois, l’option A coûtera à peu près la même chose qu’aujourd’hui, plus les nouveaux avis publiés entre-temps.
L’option B se justifie si le portail doit être remplacé ou retiré d’ici 18 mois, ou si le budget de l’année est figé.
Option C : réécriture
Nous ne la recommandons pas. Les règles métier du portail (tarification, numérotation des factures, gestion de la TVA) n’existent que dans le code, et aucune n’est testée. Une réécriture devrait les redécouvrir, et l’ancien système aurait quand même besoin de l’option B pendant la construction du nouveau.
7. Ce dont nous avons besoin pour démarrer
- Une décision entre l’option A et l’option B.
- Pour l’étape 0 : une personne disposant d’un accès administrateur à la préproduction et à la base de données, qui peut renouveler les mots de passe avec nous, en visio ou par e-mail.
- Pour l’étape 1 : des comptes nominatifs pour nous sur l’hébergement Git et en préproduction, et une personne capable de dire ce que doivent faire les six parcours critiques quand le code et les utilisateurs ne sont pas d’accord.
Annexe A : analyse complète des dépendances. La sortie complète de composer audit (JSON, les 114 avis) est livrée avec le rapport dans un fichier séparé.
Le rapport et tous les constats sont confidentiels et couverts par l’accord de confidentialité.
Comment un rapport aussi détaillé tient dans ce prix
Les outils comptent, et nous lisons le code. Les outils de programmation IA, utilisés avec votre accord écrit, accélèrent la lecture et les vérifications ; un ingénieur relit chaque constat avant qu’il n’entre dans le rapport. Plus de détails dans Notre méthode et sur la page Sécurité et confiance.
Vous voulez un rapport de ce type pour votre application ?
Code Audit à partir de 800 €, rapport sous 5 à 7 jours ouvrés. Vous hésitez encore ? Commencez par un Debt Check à 300 €.