veilletech.fr
18 sept. Feed du jour
#07 LARAVEL Article

Laravel Scalpel cherche les traces d'intrusion

La bonne question sur un serveur en production n'est pas « est-ce sûr », c'est « qui est déjà passé ».

Laravel Scalpel, de Harry Agustiana, scanne le système de fichiers d'une application Laravel déployée pour y trouver des traces d'intrusion : fichiers PHP là où il ne devrait pas y en avoir, code obfusqué, directives serveur modifiées, écarts avec un instantané de référence. Une question différente de celle des analyseurs de code habituels.

3 min de lectureintermédiairevidéo 1:18
Partager
Sommaire5 sections
  1. Ce qui se passe
  2. Les six analyseurs
  3. L'instantané, et ses conditions
  4. Le piège à connaître avant la CI
  5. À retenir

Ce qui se passe

Les analyseurs de sécurité Laravel que l'on connaît — Ward, Checkpoint — relisent le code, la configuration et les dépendances pour y trouver des problèmes. Laravel Scalpel, de Harry Agustiana, pose une autre question : des fichiers de l'application déployée ont-ils déjà été ajoutés, modifiés ou supprimés ?

Terminal
composer require hryagstn/laravel-scalpel
php artisan vendor:publish --tag=scalpel-config

PHP 8.2 ou supérieur, Laravel 10 à 13.

Les six analyseurs

scalpel:scan en exécute six par défaut. Cinq inspectent les fichiers présents, le sixième compare à un instantané.

  1. Structurel — fichiers PHP exécutables là où il ne devrait pas y en avoir, par défaut public/ et storage/. Il reconnaît .php mais aussi .phtml, .pht, .phar, et les doubles extensions du type shell.php.jpg. Sont autorisés par défaut public/index.php, public/vendor/, et les vues et caches compilés de Laravel.
  2. Code obfusqué — motifs classiques de porte dérobée : eval(base64_decode(...)), exécution de charge compressée, appels de fonction construits dynamiquement, évaluation directe de l'entrée d'une requête, longues chaînes encodées.
  3. .htaccess — mappages de handler et de type MIME autorisant l'exécution de Python, Perl ou CGI, Options +ExecCGI, réécritures vers des URL externes, et directives comme auto_prepend_file.
  4. .user.ini — directives PHP par répertoire : auto_prepend_file, auto_append_file, include_path, disable_functions. Une seule ligne y suffit pour exécuter un fichier caché à chaque requête.
  5. Environnement.env manquant, vide, illisible, lisible par tous sous Unix, ou déposé sous public/ ; APP_KEY vide ; écarts de clés avec .env.example ; APP_DEBUG=true alors que APP_ENV vaut production. L'option --production applique ce contrôle quel que soit APP_ENV.
  6. Baseline Diff — comparaison avec l'instantané enregistré.

L'instantané, et ses conditions

Terminal
php artisan scalpel:baseline
php artisan scalpel:diff

scalpel:baseline enregistre pour chaque fichier son empreinte SHA-256, sa taille et sa date de modification. À créer seulement quand l'application est dans un état de confiance : tant qu'il n'existe pas, les commandes signalent un constat MEDIUM qui réclame sa création. Après chaque déploiement, le projet conseille php artisan optimize puis scalpel:baseline --force.

Deux réglages méritent attention. L'option --fast compare d'abord taille et date avant de réutiliser l'ancienne empreinte : moins de calcul, mais un fichier modifié à taille et date préservées passe alors entre les mailles. Et la signature HMAC (SCALPEL_SIGNING_ENABLED, SCALPEL_SIGNING_KEY — une clé dédiée, pas l'APP_KEY) doit être activée avant la création du premier instantané ; scalpel:diff rejette alors en CRITICAL un instantané mal signé.

Le piège à connaître avant la CI

Testé sur Scalpel 1.9.0 dans une application Laravel 13.31 neuve, le scan par défaut ne remonte rien. Mais après un php artisan optimize, les vues compilées sous storage/framework/views ont produit 100 constats MEDIUM de variables variables et 2 constats HIGH sur des accents graves. L'analyseur structurel autorise ce répertoire, l'analyseur de code obfusqué en lit quand même le contenu. Ajouter storage/framework/views à content_scan_excluded_paths règle le problème, au prix des contrôles de contenu sur toutes les vues compilées.

En intégration continue, les formats table, json, annotations GitHub Actions et SARIF sont disponibles, et --fail-on fixe la gravité qui fait échouer le job (défaut : HIGH). Le code de sortie 0 signifie scan terminé sans constat, 1 au moins un constat au niveau demandé, 2 des constats en dessous de ce niveau ou un scan incomplet — un répertoire illisible ne passe donc pas pour un succès. Un événement ScanFinished permet enfin d'envoyer ses propres alertes sans analyser la sortie de la commande.

À retenir

Scalpel tourne dans le même processus et avec les mêmes droits que l'application : quelqu'un capable de modifier le code peut aussi modifier le scanner. Le projet recommande donc un déclenchement externe, des répertoires de code en lecture seule, et l'envoi des résultats hors du serveur concerné. C'est un détecteur de traces, pas une protection.

Source : Laravel Scalpel Scans for Filesystem Intrusion Evidence, Laravel News, 17 septembre 2026.