TP22 : Tests dynamiques de sécurité (OWASP ZAP) et tests de charge (k6)
Les scanners du TP10 lisent le code (SAST, SCA). Ils ne voient ni la configuration réelle du serveur, ni les en-têtes renvoyés, ni le comportement sous charge. Ce TP ajoute deux contrôles sur l’application en fonctionnement :
- le DAST (Dynamic Application Security Testing) avec OWASP ZAP, qui explore et attaque l’application comme le ferait un attaquant externe ;
- les tests de charge avec k6, dont les seuils (thresholds) peuvent bloquer une promotion vers la production.
⚠️ La cible, OWASP Juice Shop, est une application volontairement vulnérable. Ne scannez jamais une application qui ne vous appartient pas : un scan actif est une attaque.
Fichiers de départ :
lab-starter.zip
1. Objectifs pédagogiques
À l’issue du TP, vous serez capable de :
- situer le DAST par rapport au SAST et à la SCA, et connaître ses limites ;
- lancer les scans ZAP baseline (passif), full (actif) et API (OpenAPI) en conteneur ;
- lire un rapport ZAP, corriger une alerte et paramétrer les règles bloquantes (
rules.tsv) ; - écrire un test k6 : utilisateurs virtuels, paliers,
checks,groups,thresholds; - intégrer ZAP et k6 dans un pipeline avec un code retour bloquant.
Les images de cette page sont des schémas explicatifs, pas des captures d’écran.
2. Environnement du TP
-
VM de lab Ubuntu Server 26.04 avec Docker et Compose (TP7), au moins 4 Go de RAM libres.
-
Démarrer la cible depuis
lab-starter/:cd lab-starter docker compose up -d curl -sI http://localhost:3000 | head -5 # accès direct curl -sI http://localhost:8080 | head -5 # via le proxy Nginx
| Mode ZAP | Ce qu’il fait | Durée | Risque pour la cible |
|---|---|---|---|
| Baseline | Spider + analyse passive des réponses (en-têtes, cookies, fuites) | minutes | nul |
| Full scan | Baseline + scan actif : injections SQL, XSS, traversée de répertoires… | longue | élevé (données modifiées) |
| API scan | Comme le full scan, guidé par une définition OpenAPI / GraphQL | moyenne | élevé |
3. Partie 1 : Scan baseline
-
Lancer un scan baseline directement sur Juice Shop :
mkdir -p reports && chmod 777 reports docker run --rm --network host -v "$PWD/reports:/zap/wrk:rw" ghcr.io/zaproxy/zaproxy:stable \ zap-baseline.py -t http://localhost:3000 -r baseline-direct.html -J baseline-direct.json echo "code retour : $?" # 0 = OK, 1 = au moins un FAIL, 2 = au moins un WARN -
Ouvrir le rapport HTML. Pour trois alertes, noter : identifiant, niveau de risque, URL, preuve (evidence), solution proposée, référence CWE.
-
Parmi les alertes, lesquelles relèvent de la configuration (en-têtes, cookies) et lesquelles du code ?
4. Partie 2 : Scan actif et scan d’API
-
Lancer un full scan sur Juice Shop (comptez 15 à 30 minutes ;
-m 5limite la durée du spider) :docker run --rm --network host -v "$PWD/reports:/zap/wrk:rw" ghcr.io/zaproxy/zaproxy:stable \ zap-full-scan.py -t http://localhost:3000 -m 5 -r full.html -
Comparer avec le baseline : quelles alertes n’apparaissent qu’avec le scan actif (injection SQL, XSS…) ?
-
Lancer un scan d’API contre l’API de votre
contact-api(TP8) à partir d’une définition OpenAPI que vous écrivez (openapi.yamldécrivantGET /api/healthetPOST /api/contact) :docker run --rm --network host -v "$PWD:/zap/wrk:rw" ghcr.io/zaproxy/zaproxy:stable \ zap-api-scan.py -t /zap/wrk/openapi.yaml -f openapi -r reports/api.htmlVotre validation Zod et votre gestion d’erreurs résistent-elles ?
5. Partie 3 : Corriger des alertes
- Compléter
nginx/default.conf:server_tokens off, en-têtes de sécurité,proxy_hide_header X-Powered-By, CSP en modeReport-Only. Recharger :docker compose restart proxy. - Relancer le baseline via le proxy (
-t http://localhost:8080) et comparer le nombre d’alertes avec le scan direct :
| Alerte | Direct (3000) | Via proxy durci (8080) |
|---|---|---|
| X-Content-Type-Options manquant | ||
| Anti-clickjacking | ||
| Divulgation de version | ||
| CSP absente |
- Pourquoi la CSP passe-t-elle d’abord par
Content-Security-Policy-Report-Only? Que risque-t-on avec une CSP stricte appliquée sans test ?
6. Partie 4 : Règles bloquantes
- Générer la liste des règles (
-g gen.conf), puis compléterzap/rules.tsv:FAILpour ce qui doit bloquer,WARNpour le reste,IGNOREjustifié par un commentaire. - Relancer le baseline avec
-c rules.tsv: le code retour doit être 1 sur le scan direct et 0 sur le proxy durci.
7. Partie 5 : Tests de charge avec k6
| Notion | Signification |
|---|---|
| VU | Virtual User : boucle qui exécute la fonction default en continu. |
| stages | Paliers de charge (durée, nombre de VUs cible). |
| check | Assertion sur une réponse ; un check échoué n’arrête pas le test. |
| threshold | Critère de réussite global ; un seuil franchi donne un code retour non nul (99). |
-
Lancer le test smoke fourni :
docker run --rm -i --network host -e BASE_URL=http://localhost:8080 grafana/k6 run - < k6/smoke.js -
Compléter
k6/load.js: paliers, grouperecherche, seuils (dont un seuil par groupe etabortOnFail). Lancer le test et lire le résumé :http_req_durationp(90), p(95),http_req_failed,iterations. -
Trouver le point de rupture : augmenter progressivement les VUs (20, 50, 100…) jusqu’à franchir un seuil. Relever en parallèle la consommation CPU et mémoire (
docker stats). -
Le seuil p95 < 500 ms est-il un bon critère pour toutes les routes ? Proposer des seuils réalistes par type de route.
8. Partie 6 : Intégration continue
- Compléter le workflow
dast-perf.yml: démarrage de la cible dans le job, ZAP baseline avecrules.tsvet rapport en artefact, puis k6 smoke et load avec seuils bloquants. - Ouvrir une Pull Request qui retire un en-tête de sécurité du proxy : le job doit échouer. Expliquer où placer ces contrôles dans une chaîne
DEV → STAGING → PRODUCTION.
Défi
- Faire un scan ZAP authentifié sur Juice Shop (contexte ZAP, script de connexion ou en-tête
Authorizationvia-z "-config replacer...") et comparer la couverture.- Envoyer les métriques k6 vers le Prometheus du TP21 (
-o experimental-prometheus-rw) et les afficher dans Grafana à côté des métriques serveur.
9. Livrables
Captures à déposer dans captures/ :
| Fichier | Contenu attendu |
|---|---|
zap-baseline.png |
Rapport baseline (résumé des alertes) |
zap-full.png |
Alertes supplémentaires du scan actif |
zap-before-after.png |
Alertes direct / via proxy durci |
zap-rules-exit.png |
Codes retour 1 puis 0 avec rules.tsv |
k6-summary.png |
Résumé k6 avec les seuils (✓ / ✗) |
k6-breaking-point.png |
Seuil franchi lors de la recherche du point de rupture |
ci-dast-perf.png |
Workflow en échec puis réussi |
Ainsi que : default.conf, rules.tsv, load.js, openapi.yaml, dast-perf.yml et les tableaux remplis.