Un article de Laravel News publié le 17 septembre 2026 présente Laravel Scalpel, un paquet créé par Harry Agustiana. Il s’exécute dans une application Laravel et recherche des indices d’intrusion dans le système de fichiers, notamment des fichiers PHP ajoutés, du code dissimulé, des directives serveur modifiées et des changements intervenus après le déploiement.
php artisan scalpel:scan
php artisan scalpel:scan --only=structural,obfuscated
php artisan scalpel:scan --only=useriniWard et Checkpoint répondent à un autre besoin. Ces outils examinent le code, la configuration et les dépendances à la recherche de failles. Scalpel cherche les fichiers qui ont pu être ajoutés, modifiés ou supprimés dans une application déjà déployée.
La commande scalpel:scan lance six contrôles par défaut. Cinq inspectent l’état actuel des fichiers, tandis que Baseline Diff le compare à un instantané enregistré. Le scanner structurel examine public/ et storage/ pour y trouver des fichiers PHP exécutables. Il reconnaît les extensions .php, .phtml, .pht et .phar, ainsi que des noms à double extension comme shell.php.jpg. public/index.php, public/vendor/ et les vues et caches compilés de Laravel sont autorisés par défaut. Les répertoires analysés et les listes d’exclusion peuvent être modifiés dans la configuration.
Le scanner de code obfusqué recherche notamment eval(base64_decode(...)), l’exécution de charges utiles compressées, les appels de fonctions dynamiques, l’évaluation directe de données issues des requêtes et les longues chaînes encodées. Un motif peut être désactivé dans config/scalpel.php lorsqu’un code légitime produit une alerte. Le scanner .htaccess signale les associations de handlers et de types MIME qui permettent d’exécuter Python, Perl, CGI ou d’autres scripts. Il contrôle aussi Options +ExecCGI, les réécritures vers des URL externes et des directives comme auto_prepend_file. Le scanner .user.ini couvre auto_prepend_file, auto_append_file, include_path et disable_functions. Un attaquant peut utiliser auto_prepend_file pour lancer un fichier caché à chaque requête.
Le contrôle de l’environnement signale un fichier .env absent, vide ou illisible, un fichier .env placé sous public/ et, sur les systèmes de type Unix, un fichier lisible par tous. Il détecte aussi un APP_KEY vide et compare les clés de .env avec celles de .env.example. Les clés manquantes ou inattendues sont considérées comme des indices possibles d’intrusion. Lorsque APP_ENV vaut production, APP_DEBUG=true déclenche une alerte. L’option --production force ce contrôle quelle que soit la valeur de APP_ENV et signale aussi APP_ENV=local.
scalpel:scan accepte l’option --only pour sélectionner des contrôles. scalpel:baseline enregistre, pour chaque fichier inclus, un hash SHA-256, la taille et la date de modification. scalpel:diff signale les fichiers ajoutés, modifiés et supprimés. La référence doit être créée lorsque l’application est dans un état jugé fiable. Avant sa création, scalpel:scan et scalpel:diff génèrent une alerte MEDIUM demandant une baseline. Après chaque déploiement, la séquence recommandée utilise php artisan optimize, puis scalpel:baseline --force.
Les exclusions par défaut couvrent les chemins qui changent souvent, comme les logs, les sessions, les vues compilées et storage/app. Le dossier vendor/ est ignoré par les contrôles de contenu, mais reste inclus dans les comparaisons de baseline. Un fichier ajouté à un paquet installé apparaît donc dans le diff. Le mode strict recalcule le hash de chaque fichier à chaque comparaison. --fast réutilise l’ancien hash lorsque la taille et la date de modification n’ont pas changé. Cette optimisation peut laisser passer une modification dont l’attaquant a conservé ces deux valeurs. Un autre scanner peut encore la repérer grâce au contenu ou à l’emplacement du fichier.
Scalpel peut signer les baselines et les rapports JSON avec un HMAC. SCALPEL_SIGNING_ENABLED doit être activé et SCALPEL_SIGNING_KEY doit contenir une clé dédiée, différente de APP_KEY. scalpel:diff vérifie une baseline signée et classe une signature absente ou invalide comme CRITICAL. Cette protection ne couvre pas une clé accessible à l’attaquant ni un scanner modifié. La signature doit être activée avant la création de la première baseline. scalpel:verify contrôle la signature d’un rapport JSON enregistré.
Le test décrit dans l’article utilise Scalpel 1.9.0 dans une nouvelle application Laravel 13.31. Avec des permissions .env réglées sur 0600 et une baseline existante, le scan par défaut ne remonte aucun problème. Quatre éléments de test sans danger sont ensuite ajoutés : eval(base64_decode(...)) dans un bloc if (false), un fichier nommé avatar.php.jpg, une association de handler dans .htaccess et un fichier .user.ini contenant auto_prepend_file. Les quatre scanners de contenu sont lancés avec php artisan scalpel:scan --only=structural,obfuscated,htaccess,userini --no-banner. Un scan complet remonte neuf résultats, car Baseline Diff voit aussi ces quatre fichiers et une modification de routes/web.php.
L’essai met en évidence un problème possible avec les vues générées en CI. php artisan optimize compile des vues du framework dans storage/framework/views. Le scan suivant y trouve 100 résultats MEDIUM liés à des variables variables et deux résultats HIGH liés à des backticks. Après php artisan optimize:clear et la recréation de la baseline, le scan ne remonte plus rien. Le scanner structurel autorise ce répertoire, mais le scanner de code obfusqué en lit toujours le contenu. Les applications qui mettent les vues en cache en production doivent vérifier ce chemin avant d’utiliser Scalpel comme étape bloquante du déploiement. Ajouter storage/framework/views à content_scan_excluded_paths supprime ces alertes et désactive les contrôles de contenu pour toutes les vues compilées.
scan et diff proposent des sorties en tableau, JSON, annotations GitHub Actions et SARIF. --fail-on définit le niveau minimal qui fait échouer un job CI. HIGH est la valeur par défaut. L’exemple SARIF fourni est php artisan scalpel:scan --format=sarif --fail-on=MEDIUM. Le code de sortie 0 indique qu’aucun résultat au niveau demandé n’a été trouvé. Le code 1 signale un résultat atteignant le seuil. Le code 2 correspond à des résultats moins graves ou à un scan incomplet, par exemple lorsqu’un répertoire est illisible.
Après scalpel:scan ou scalpel:diff, le paquet déclenche l’événement ScanFinished. Celui-ci contient les résultats, la commande exécutée, scan ou diff, et la durée en millisecondes. Une application peut s’en servir pour envoyer ses propres alertes par e-mail, Slack ou webhook sans analyser la sortie du terminal.
Laravel Scalpel exige PHP 8.2 ou une version ultérieure et prend en charge Laravel 10 à 13. L’installation utilise composer require hryagstn/laravel-scalpel, puis php artisan vendor:publish --tag=scalpel-config pour publier la configuration. Le scanner partage le processus et les permissions du système de fichiers de l’application. Une personne capable de modifier le code peut aussi modifier le scanner ou sa configuration. Le projet recommande des déclencheurs externes, des répertoires de code en lecture seule et l’envoi des résultats vers un stockage hors du serveur potentiellement compromis. Scalpel détecte des indices d’intrusion et ne fournit ni pare-feu ni mécanisme de confinement. Le code source et la référence des commandes sont disponibles sur GitHub, avec un simulateur sur le site Laravel Scalpel.




Commentaires
Pas encore de commentaire — écris le premier.
Lance la discussion
Pas de compte ni de mot de passe — saisis simplement ton adresse e-mail et nous t’envoyons un lien de connexion à usage unique. Première visite ? Tout se met en place automatiquement.
Ton évaluation sera appliquée automatiquement après ta connexion.
Vérifie ta boîte mail
Nous avons envoyé un lien de connexion à …. Ouvre-le sur cet appareil — cet onglet te connectera automatiquement.
Rien reçu ? Vérifiez le dossier spam — et marquez le message « Non spam » pour qu'il arrive directement la prochaine fois.