From 2eb058a9bb8e455103f17fd9fc86cdc7bb996f85 Mon Sep 17 00:00:00 2001 From: Emmanuel Gautier Date: Sun, 12 Jul 2026 23:38:13 +0200 Subject: [PATCH] docs: translate docs in french --- docs/{ => en}/best-practices/_meta.yml | 0 docs/{ => en}/best-practices/jwt.mdx | 0 .../best-practices/security-headers.mdx | 0 docs/{ => en}/first-scan.mdx | 0 docs/{ => en}/getting-started.mdx | 0 docs/{ => en}/github-action.mdx | 0 docs/{ => en}/index.mdx | 2 +- docs/{ => en}/installation.mdx | 0 docs/{ => en}/labs.mdx | 0 docs/{ => en}/reference/_meta.yml | 0 docs/{ => en}/reference/cli/_meta.yml | 0 docs/{ => en}/reference/cli/discover-api.mdx | 0 .../reference/cli/discover-domain.mdx | 0 docs/{ => en}/reference/cli/index.mdx | 0 docs/{ => en}/reference/cli/jwt-generate.mdx | 0 docs/{ => en}/reference/cli/scan-curl.mdx | 0 docs/{ => en}/reference/cli/scan-graphql.mdx | 0 docs/{ => en}/reference/cli/scan-openapi.mdx | 0 docs/{ => en}/reference/cli/serve.mdx | 0 docs/{ => en}/reference/output-formats.mdx | 0 docs/{ => en}/reference/scan-ids.mdx | 0 docs/{ => en}/vampi.mdx | 0 docs/{ => en}/vulnerabilities.mdx | 0 docs/{ => en}/vulnerabilities/_meta.yml | 0 .../broken-authentication/_meta.yml | 0 .../authentication-bypass.mdx | 0 .../brute-force-attack.mdx | 0 .../broken-authentication/jwt-alg-none.mdx | 0 .../jwt-algorithm-confusion.mdx | 0 .../jwt-blank-secret.mdx | 0 .../jwt-cross-service-relay-attack.excalidraw | 0 .../jwt-cross-service-relay-attack.mdx | 0 .../jwt-cross-service-relay-attack.png | Bin .../broken-authentication/jwt-expired.mdx | 0 .../jwt-kid-injection.mdx | 0 .../jwt-not-verified.mdx | 0 .../jwt-null-signature.mdx | 0 .../broken-authentication/jwt-weak-secret.mdx | 0 .../security-misconfiguration/_meta.yml | 0 .../graphql-introspection.mdx | 0 .../http-cookies.mdx | 0 .../http-method-allow-override.mdx | 0 .../http-trace-track.mdx | 0 .../security-misconfiguration/tls.mdx | 0 docs/fr/best-practices/_meta.yml | 1 + docs/fr/best-practices/jwt.mdx | 100 ++ docs/fr/best-practices/security-headers.mdx | 102 ++ docs/fr/first-scan.mdx | 83 ++ docs/fr/getting-started.mdx | 29 + docs/fr/github-action.mdx | 40 + docs/fr/index.mdx | 124 ++ docs/fr/installation.mdx | 122 ++ docs/fr/labs.mdx | 20 + docs/fr/reference/_meta.yml | 1 + docs/fr/reference/cli/_meta.yml | 1 + docs/fr/reference/cli/discover-api.mdx | 41 + docs/fr/reference/cli/discover-domain.mdx | 33 + docs/fr/reference/cli/index.mdx | 65 + docs/fr/reference/cli/jwt-generate.mdx | 48 + docs/fr/reference/cli/scan-curl.mdx | 59 + docs/fr/reference/cli/scan-graphql.mdx | 45 + docs/fr/reference/cli/scan-openapi.mdx | 61 + docs/fr/reference/cli/serve.mdx | 37 + docs/fr/reference/output-formats.mdx | 93 ++ docs/fr/reference/scan-ids.mdx | 80 ++ docs/fr/vampi.mdx | 89 ++ docs/fr/vulnerabilities.mdx | 48 + docs/fr/vulnerabilities/_meta.yml | 1 + .../broken-authentication/_meta.yml | 1 + .../authentication-bypass.mdx | 103 ++ .../brute-force-attack.mdx | 58 + .../broken-authentication/jwt-alg-none.mdx | 124 ++ .../jwt-algorithm-confusion.mdx | 90 ++ .../jwt-blank-secret.mdx | 104 ++ .../jwt-cross-service-relay-attack.excalidraw | 1235 +++++++++++++++++ .../jwt-cross-service-relay-attack.mdx | 115 ++ .../jwt-cross-service-relay-attack.png | Bin 0 -> 100233 bytes .../broken-authentication/jwt-expired.mdx | 66 + .../jwt-kid-injection.mdx | 99 ++ .../jwt-not-verified.mdx | 108 ++ .../jwt-null-signature.mdx | 107 ++ .../broken-authentication/jwt-weak-secret.mdx | 108 ++ .../security-misconfiguration/_meta.yml | 1 + .../graphql-introspection.mdx | 56 + .../http-cookies.mdx | 127 ++ .../http-method-allow-override.mdx | 79 ++ .../http-trace-track.mdx | 123 ++ .../security-misconfiguration/tls.mdx | 71 + 88 files changed, 4099 insertions(+), 1 deletion(-) rename docs/{ => en}/best-practices/_meta.yml (100%) rename docs/{ => en}/best-practices/jwt.mdx (100%) rename docs/{ => en}/best-practices/security-headers.mdx (100%) rename docs/{ => en}/first-scan.mdx (100%) rename docs/{ => en}/getting-started.mdx (100%) rename docs/{ => en}/github-action.mdx (100%) rename docs/{ => en}/index.mdx (99%) rename docs/{ => en}/installation.mdx (100%) rename docs/{ => en}/labs.mdx (100%) rename docs/{ => en}/reference/_meta.yml (100%) rename docs/{ => en}/reference/cli/_meta.yml (100%) rename docs/{ => en}/reference/cli/discover-api.mdx (100%) rename docs/{ => en}/reference/cli/discover-domain.mdx (100%) rename docs/{ => en}/reference/cli/index.mdx (100%) rename docs/{ => en}/reference/cli/jwt-generate.mdx (100%) rename docs/{ => en}/reference/cli/scan-curl.mdx (100%) rename docs/{ => en}/reference/cli/scan-graphql.mdx (100%) rename docs/{ => en}/reference/cli/scan-openapi.mdx (100%) rename docs/{ => en}/reference/cli/serve.mdx (100%) rename docs/{ => en}/reference/output-formats.mdx (100%) rename docs/{ => en}/reference/scan-ids.mdx (100%) rename docs/{ => en}/vampi.mdx (100%) rename docs/{ => en}/vulnerabilities.mdx (100%) rename docs/{ => en}/vulnerabilities/_meta.yml (100%) rename docs/{ => en}/vulnerabilities/broken-authentication/_meta.yml (100%) rename docs/{ => en}/vulnerabilities/broken-authentication/authentication-bypass.mdx (100%) rename docs/{ => en}/vulnerabilities/broken-authentication/brute-force-attack.mdx (100%) rename docs/{ => en}/vulnerabilities/broken-authentication/jwt-alg-none.mdx (100%) rename docs/{ => en}/vulnerabilities/broken-authentication/jwt-algorithm-confusion.mdx (100%) rename docs/{ => en}/vulnerabilities/broken-authentication/jwt-blank-secret.mdx (100%) rename docs/{ => en}/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.excalidraw (100%) rename docs/{ => en}/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.mdx (100%) rename docs/{ => en}/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.png (100%) rename docs/{ => en}/vulnerabilities/broken-authentication/jwt-expired.mdx (100%) rename docs/{ => en}/vulnerabilities/broken-authentication/jwt-kid-injection.mdx (100%) rename docs/{ => en}/vulnerabilities/broken-authentication/jwt-not-verified.mdx (100%) rename docs/{ => en}/vulnerabilities/broken-authentication/jwt-null-signature.mdx (100%) rename docs/{ => en}/vulnerabilities/broken-authentication/jwt-weak-secret.mdx (100%) rename docs/{ => en}/vulnerabilities/security-misconfiguration/_meta.yml (100%) rename docs/{ => en}/vulnerabilities/security-misconfiguration/graphql-introspection.mdx (100%) rename docs/{ => en}/vulnerabilities/security-misconfiguration/http-cookies.mdx (100%) rename docs/{ => en}/vulnerabilities/security-misconfiguration/http-method-allow-override.mdx (100%) rename docs/{ => en}/vulnerabilities/security-misconfiguration/http-trace-track.mdx (100%) rename docs/{ => en}/vulnerabilities/security-misconfiguration/tls.mdx (100%) create mode 100644 docs/fr/best-practices/_meta.yml create mode 100644 docs/fr/best-practices/jwt.mdx create mode 100644 docs/fr/best-practices/security-headers.mdx create mode 100644 docs/fr/first-scan.mdx create mode 100644 docs/fr/getting-started.mdx create mode 100644 docs/fr/github-action.mdx create mode 100644 docs/fr/index.mdx create mode 100644 docs/fr/installation.mdx create mode 100644 docs/fr/labs.mdx create mode 100644 docs/fr/reference/_meta.yml create mode 100644 docs/fr/reference/cli/_meta.yml create mode 100644 docs/fr/reference/cli/discover-api.mdx create mode 100644 docs/fr/reference/cli/discover-domain.mdx create mode 100644 docs/fr/reference/cli/index.mdx create mode 100644 docs/fr/reference/cli/jwt-generate.mdx create mode 100644 docs/fr/reference/cli/scan-curl.mdx create mode 100644 docs/fr/reference/cli/scan-graphql.mdx create mode 100644 docs/fr/reference/cli/scan-openapi.mdx create mode 100644 docs/fr/reference/cli/serve.mdx create mode 100644 docs/fr/reference/output-formats.mdx create mode 100644 docs/fr/reference/scan-ids.mdx create mode 100644 docs/fr/vampi.mdx create mode 100644 docs/fr/vulnerabilities.mdx create mode 100644 docs/fr/vulnerabilities/_meta.yml create mode 100644 docs/fr/vulnerabilities/broken-authentication/_meta.yml create mode 100644 docs/fr/vulnerabilities/broken-authentication/authentication-bypass.mdx create mode 100644 docs/fr/vulnerabilities/broken-authentication/brute-force-attack.mdx create mode 100644 docs/fr/vulnerabilities/broken-authentication/jwt-alg-none.mdx create mode 100644 docs/fr/vulnerabilities/broken-authentication/jwt-algorithm-confusion.mdx create mode 100644 docs/fr/vulnerabilities/broken-authentication/jwt-blank-secret.mdx create mode 100644 docs/fr/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.excalidraw create mode 100644 docs/fr/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.mdx create mode 100644 docs/fr/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.png create mode 100644 docs/fr/vulnerabilities/broken-authentication/jwt-expired.mdx create mode 100644 docs/fr/vulnerabilities/broken-authentication/jwt-kid-injection.mdx create mode 100644 docs/fr/vulnerabilities/broken-authentication/jwt-not-verified.mdx create mode 100644 docs/fr/vulnerabilities/broken-authentication/jwt-null-signature.mdx create mode 100644 docs/fr/vulnerabilities/broken-authentication/jwt-weak-secret.mdx create mode 100644 docs/fr/vulnerabilities/security-misconfiguration/_meta.yml create mode 100644 docs/fr/vulnerabilities/security-misconfiguration/graphql-introspection.mdx create mode 100644 docs/fr/vulnerabilities/security-misconfiguration/http-cookies.mdx create mode 100644 docs/fr/vulnerabilities/security-misconfiguration/http-method-allow-override.mdx create mode 100644 docs/fr/vulnerabilities/security-misconfiguration/http-trace-track.mdx create mode 100644 docs/fr/vulnerabilities/security-misconfiguration/tls.mdx diff --git a/docs/best-practices/_meta.yml b/docs/en/best-practices/_meta.yml similarity index 100% rename from docs/best-practices/_meta.yml rename to docs/en/best-practices/_meta.yml diff --git a/docs/best-practices/jwt.mdx b/docs/en/best-practices/jwt.mdx similarity index 100% rename from docs/best-practices/jwt.mdx rename to docs/en/best-practices/jwt.mdx diff --git a/docs/best-practices/security-headers.mdx b/docs/en/best-practices/security-headers.mdx similarity index 100% rename from docs/best-practices/security-headers.mdx rename to docs/en/best-practices/security-headers.mdx diff --git a/docs/first-scan.mdx b/docs/en/first-scan.mdx similarity index 100% rename from docs/first-scan.mdx rename to docs/en/first-scan.mdx diff --git a/docs/getting-started.mdx b/docs/en/getting-started.mdx similarity index 100% rename from docs/getting-started.mdx rename to docs/en/getting-started.mdx diff --git a/docs/github-action.mdx b/docs/en/github-action.mdx similarity index 100% rename from docs/github-action.mdx rename to docs/en/github-action.mdx diff --git a/docs/index.mdx b/docs/en/index.mdx similarity index 99% rename from docs/index.mdx rename to docs/en/index.mdx index 7b88a8c9..788052b0 100644 --- a/docs/index.mdx +++ b/docs/en/index.mdx @@ -9,7 +9,7 @@ import { Aside } from '@/components/docs' Welcome to VulnAPI Documentation! -![Demo](../demo.gif) +![Demo](../../demo.gif) --- diff --git a/docs/installation.mdx b/docs/en/installation.mdx similarity index 100% rename from docs/installation.mdx rename to docs/en/installation.mdx diff --git a/docs/labs.mdx b/docs/en/labs.mdx similarity index 100% rename from docs/labs.mdx rename to docs/en/labs.mdx diff --git a/docs/reference/_meta.yml b/docs/en/reference/_meta.yml similarity index 100% rename from docs/reference/_meta.yml rename to docs/en/reference/_meta.yml diff --git a/docs/reference/cli/_meta.yml b/docs/en/reference/cli/_meta.yml similarity index 100% rename from docs/reference/cli/_meta.yml rename to docs/en/reference/cli/_meta.yml diff --git a/docs/reference/cli/discover-api.mdx b/docs/en/reference/cli/discover-api.mdx similarity index 100% rename from docs/reference/cli/discover-api.mdx rename to docs/en/reference/cli/discover-api.mdx diff --git a/docs/reference/cli/discover-domain.mdx b/docs/en/reference/cli/discover-domain.mdx similarity index 100% rename from docs/reference/cli/discover-domain.mdx rename to docs/en/reference/cli/discover-domain.mdx diff --git a/docs/reference/cli/index.mdx b/docs/en/reference/cli/index.mdx similarity index 100% rename from docs/reference/cli/index.mdx rename to docs/en/reference/cli/index.mdx diff --git a/docs/reference/cli/jwt-generate.mdx b/docs/en/reference/cli/jwt-generate.mdx similarity index 100% rename from docs/reference/cli/jwt-generate.mdx rename to docs/en/reference/cli/jwt-generate.mdx diff --git a/docs/reference/cli/scan-curl.mdx b/docs/en/reference/cli/scan-curl.mdx similarity index 100% rename from docs/reference/cli/scan-curl.mdx rename to docs/en/reference/cli/scan-curl.mdx diff --git a/docs/reference/cli/scan-graphql.mdx b/docs/en/reference/cli/scan-graphql.mdx similarity index 100% rename from docs/reference/cli/scan-graphql.mdx rename to docs/en/reference/cli/scan-graphql.mdx diff --git a/docs/reference/cli/scan-openapi.mdx b/docs/en/reference/cli/scan-openapi.mdx similarity index 100% rename from docs/reference/cli/scan-openapi.mdx rename to docs/en/reference/cli/scan-openapi.mdx diff --git a/docs/reference/cli/serve.mdx b/docs/en/reference/cli/serve.mdx similarity index 100% rename from docs/reference/cli/serve.mdx rename to docs/en/reference/cli/serve.mdx diff --git a/docs/reference/output-formats.mdx b/docs/en/reference/output-formats.mdx similarity index 100% rename from docs/reference/output-formats.mdx rename to docs/en/reference/output-formats.mdx diff --git a/docs/reference/scan-ids.mdx b/docs/en/reference/scan-ids.mdx similarity index 100% rename from docs/reference/scan-ids.mdx rename to docs/en/reference/scan-ids.mdx diff --git a/docs/vampi.mdx b/docs/en/vampi.mdx similarity index 100% rename from docs/vampi.mdx rename to docs/en/vampi.mdx diff --git a/docs/vulnerabilities.mdx b/docs/en/vulnerabilities.mdx similarity index 100% rename from docs/vulnerabilities.mdx rename to docs/en/vulnerabilities.mdx diff --git a/docs/vulnerabilities/_meta.yml b/docs/en/vulnerabilities/_meta.yml similarity index 100% rename from docs/vulnerabilities/_meta.yml rename to docs/en/vulnerabilities/_meta.yml diff --git a/docs/vulnerabilities/broken-authentication/_meta.yml b/docs/en/vulnerabilities/broken-authentication/_meta.yml similarity index 100% rename from docs/vulnerabilities/broken-authentication/_meta.yml rename to docs/en/vulnerabilities/broken-authentication/_meta.yml diff --git a/docs/vulnerabilities/broken-authentication/authentication-bypass.mdx b/docs/en/vulnerabilities/broken-authentication/authentication-bypass.mdx similarity index 100% rename from docs/vulnerabilities/broken-authentication/authentication-bypass.mdx rename to docs/en/vulnerabilities/broken-authentication/authentication-bypass.mdx diff --git a/docs/vulnerabilities/broken-authentication/brute-force-attack.mdx b/docs/en/vulnerabilities/broken-authentication/brute-force-attack.mdx similarity index 100% rename from docs/vulnerabilities/broken-authentication/brute-force-attack.mdx rename to docs/en/vulnerabilities/broken-authentication/brute-force-attack.mdx diff --git a/docs/vulnerabilities/broken-authentication/jwt-alg-none.mdx b/docs/en/vulnerabilities/broken-authentication/jwt-alg-none.mdx similarity index 100% rename from docs/vulnerabilities/broken-authentication/jwt-alg-none.mdx rename to docs/en/vulnerabilities/broken-authentication/jwt-alg-none.mdx diff --git a/docs/vulnerabilities/broken-authentication/jwt-algorithm-confusion.mdx b/docs/en/vulnerabilities/broken-authentication/jwt-algorithm-confusion.mdx similarity index 100% rename from docs/vulnerabilities/broken-authentication/jwt-algorithm-confusion.mdx rename to docs/en/vulnerabilities/broken-authentication/jwt-algorithm-confusion.mdx diff --git a/docs/vulnerabilities/broken-authentication/jwt-blank-secret.mdx b/docs/en/vulnerabilities/broken-authentication/jwt-blank-secret.mdx similarity index 100% rename from docs/vulnerabilities/broken-authentication/jwt-blank-secret.mdx rename to docs/en/vulnerabilities/broken-authentication/jwt-blank-secret.mdx diff --git a/docs/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.excalidraw b/docs/en/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.excalidraw similarity index 100% rename from docs/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.excalidraw rename to docs/en/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.excalidraw diff --git a/docs/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.mdx b/docs/en/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.mdx similarity index 100% rename from docs/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.mdx rename to docs/en/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.mdx diff --git a/docs/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.png b/docs/en/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.png similarity index 100% rename from docs/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.png rename to docs/en/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.png diff --git a/docs/vulnerabilities/broken-authentication/jwt-expired.mdx b/docs/en/vulnerabilities/broken-authentication/jwt-expired.mdx similarity index 100% rename from docs/vulnerabilities/broken-authentication/jwt-expired.mdx rename to docs/en/vulnerabilities/broken-authentication/jwt-expired.mdx diff --git a/docs/vulnerabilities/broken-authentication/jwt-kid-injection.mdx b/docs/en/vulnerabilities/broken-authentication/jwt-kid-injection.mdx similarity index 100% rename from docs/vulnerabilities/broken-authentication/jwt-kid-injection.mdx rename to docs/en/vulnerabilities/broken-authentication/jwt-kid-injection.mdx diff --git a/docs/vulnerabilities/broken-authentication/jwt-not-verified.mdx b/docs/en/vulnerabilities/broken-authentication/jwt-not-verified.mdx similarity index 100% rename from docs/vulnerabilities/broken-authentication/jwt-not-verified.mdx rename to docs/en/vulnerabilities/broken-authentication/jwt-not-verified.mdx diff --git a/docs/vulnerabilities/broken-authentication/jwt-null-signature.mdx b/docs/en/vulnerabilities/broken-authentication/jwt-null-signature.mdx similarity index 100% rename from docs/vulnerabilities/broken-authentication/jwt-null-signature.mdx rename to docs/en/vulnerabilities/broken-authentication/jwt-null-signature.mdx diff --git a/docs/vulnerabilities/broken-authentication/jwt-weak-secret.mdx b/docs/en/vulnerabilities/broken-authentication/jwt-weak-secret.mdx similarity index 100% rename from docs/vulnerabilities/broken-authentication/jwt-weak-secret.mdx rename to docs/en/vulnerabilities/broken-authentication/jwt-weak-secret.mdx diff --git a/docs/vulnerabilities/security-misconfiguration/_meta.yml b/docs/en/vulnerabilities/security-misconfiguration/_meta.yml similarity index 100% rename from docs/vulnerabilities/security-misconfiguration/_meta.yml rename to docs/en/vulnerabilities/security-misconfiguration/_meta.yml diff --git a/docs/vulnerabilities/security-misconfiguration/graphql-introspection.mdx b/docs/en/vulnerabilities/security-misconfiguration/graphql-introspection.mdx similarity index 100% rename from docs/vulnerabilities/security-misconfiguration/graphql-introspection.mdx rename to docs/en/vulnerabilities/security-misconfiguration/graphql-introspection.mdx diff --git a/docs/vulnerabilities/security-misconfiguration/http-cookies.mdx b/docs/en/vulnerabilities/security-misconfiguration/http-cookies.mdx similarity index 100% rename from docs/vulnerabilities/security-misconfiguration/http-cookies.mdx rename to docs/en/vulnerabilities/security-misconfiguration/http-cookies.mdx diff --git a/docs/vulnerabilities/security-misconfiguration/http-method-allow-override.mdx b/docs/en/vulnerabilities/security-misconfiguration/http-method-allow-override.mdx similarity index 100% rename from docs/vulnerabilities/security-misconfiguration/http-method-allow-override.mdx rename to docs/en/vulnerabilities/security-misconfiguration/http-method-allow-override.mdx diff --git a/docs/vulnerabilities/security-misconfiguration/http-trace-track.mdx b/docs/en/vulnerabilities/security-misconfiguration/http-trace-track.mdx similarity index 100% rename from docs/vulnerabilities/security-misconfiguration/http-trace-track.mdx rename to docs/en/vulnerabilities/security-misconfiguration/http-trace-track.mdx diff --git a/docs/vulnerabilities/security-misconfiguration/tls.mdx b/docs/en/vulnerabilities/security-misconfiguration/tls.mdx similarity index 100% rename from docs/vulnerabilities/security-misconfiguration/tls.mdx rename to docs/en/vulnerabilities/security-misconfiguration/tls.mdx diff --git a/docs/fr/best-practices/_meta.yml b/docs/fr/best-practices/_meta.yml new file mode 100644 index 00000000..481636a9 --- /dev/null +++ b/docs/fr/best-practices/_meta.yml @@ -0,0 +1 @@ +label: Bonnes pratiques diff --git a/docs/fr/best-practices/jwt.mdx b/docs/fr/best-practices/jwt.mdx new file mode 100644 index 00000000..4004d4ec --- /dev/null +++ b/docs/fr/best-practices/jwt.mdx @@ -0,0 +1,100 @@ +--- +title: Bonnes pratiques de sécurité JWT +description: Bonnes pratiques pour implémenter de manière sécurisée les JSON Web Tokens (JWT) dans vos API. +--- + +Les JSON Web Tokens (JWT) sont largement utilisés pour l'authentification et l'autorisation dans les API. Lorsqu'ils sont mal implémentés, ils deviennent une surface d'attaque à haute sévérité. Ce guide présente les meilleures pratiques les plus importantes et associe chaque point à l'analyse VulnAPI correspondante. + +## Utiliser des algorithmes de signature forts + +Privilégiez les algorithmes asymétriques (RS256, ES256) pour les systèmes distribués où plusieurs services vérifient les jetons mais un seul les émet. Cela garantit que la clé privée ne quitte jamais l'émetteur. + +- **Recommandé** : RS256, RS384, RS512, ES256, ES384 +- **Acceptable pour les systèmes à service unique** : HS256, HS384, HS512 (avec un secret fort — voir ci-dessous) +- **À ne jamais utiliser** : `alg: none` en production + +L'utilisation de `alg: none` supprime toute protection cryptographique, rendant les jetons facilement falsifiables. VulnAPI détecte cela avec l'analyse [`jwt.alg_none`](../vulnerabilities/broken-authentication/jwt-alg-none). + +## Utiliser un secret aléatoire d'au moins 256 bits pour HMAC + +Lors de l'utilisation d'algorithmes HMAC (HS256/HS384/HS512), la clé secrète doit être : + +- Au moins 256 bits (32 octets) de données cryptographiquement aléatoires +- Pas un mot du dictionnaire, un défaut bien connu ou une chaîne prévisible +- Pas une chaîne vide + +Les jetons signés avec des secrets vides ou faibles sont détectés par les analyses [`jwt.blank_secret`](../vulnerabilities/broken-authentication/jwt-blank-secret) et [`jwt.weak_secret`](../vulnerabilities/broken-authentication/jwt-weak-secret). + +```bash +# Générer un secret sécurisé de 256 bits (Linux/macOS) +openssl rand -base64 32 +``` + +## Toujours vérifier les signatures côté serveur + +Ne faites jamais confiance à un JWT sans vérifier sa signature à l'aide de la clé appropriée. L'omission de la vérification des signatures est l'une des erreurs d'implémentation les plus dangereuses. + +VulnAPI détecte cela avec l'analyse [`jwt.not_verified`](../vulnerabilities/broken-authentication/jwt-not-verified) (CVSS 9.3). + +```python +# Exemple Python avec PyJWT — passez toujours explicitement les algorithmes +import jwt +payload = jwt.decode(token, public_key, algorithms=["RS256"]) +``` + +## Rejeter les jetons avec une signature supprimée + +Certaines bibliothèques JWT acceptent des jetons dont le segment de signature est vide (signature nulle). Configurez toujours votre bibliothèque pour exiger une signature valide et non vide. + +VulnAPI détecte cela avec l'analyse [`jwt.null_signature`](../vulnerabilities/broken-authentication/jwt-null-signature). + +## Valider les revendications standards (Claims) + +Après avoir vérifié la signature, valisez ces revendications avant de faire confiance aux données de la charge utile (payload) : + +| Revendication | Description | Action | +|---------------|-------------|--------| +| `iss` | Émetteur (Issuer) — qui a créé le jeton | Rejeter s'il ne provient pas d'un émetteur de confiance | +| `aud` | Audience — à qui le jeton est destiné | Rejeter si votre service n'est pas l'audience visée | +| `exp` | Date d'expiration | Rejeter si l'heure actuelle est postérieure à `exp` | +| `nbf` | Date d'effet (Not-before) | Rejeter si l'heure actuelle est antérieure à `nbf` | +| `sub` | Sujet (Subject) — qui le jeton représente | Vérifier que l'utilisateur existe toujours / est actif | + +Le fait de ne pas valider `iss` et `aud` permet des [attaques par relais inter-services](../vulnerabilities/broken-authentication/jwt-cross-service-relay-attack). + +## Utiliser des durées de vie de jeton courtes avec rotation des jetons de rafraîchissement (Refresh Tokens) + +Les jetons d'accès à courte durée de vie limitent les dommages causés par un jeton volé : + +- **Jetons d'accès** : 5 à 15 minutes +- **Jetons de rafraîchissement** : de quelques heures à plusieurs jours, à usage unique (rotation à chaque utilisation) + +Effectuez une rotation des jetons de rafraîchissement à chaque utilisation pour détecter le vol de jeton (si un jeton de rafraîchissement déjà utilisé est présenté, révoquez toute la session). + +## Ne jamais utiliser `alg: none` en production + +La spécification JWT autorise `alg: none` pour les jetons non sécurisés (par exemple, les messages échangés localement). Cela ne doit jamais être accepté par un point de terminaison d'API de production. + +Sécurisez votre bibliothèque JWT pour rejeter explicitement `none` : + +```go +// Exemple Go utilisant golang-jwt +token, err := jwt.Parse(tokenString, keyFunc, jwt.WithValidMethods([]string{"RS256", "ES256"})) +``` + +## Stocker les jetons de manière sécurisée sur le client + +| Stockage | Risque XSS | Risque CSRF | Recommandation | +|----------|------------|-------------|----------------| +| `localStorage` | Élevé (lisible par JS) | Aucun | À éviter pour les jetons sensibles | +| `sessionStorage` | Élevé (lisible par JS) | Aucun | À éviter pour les jetons sensibles | +| Cookie `HttpOnly` | Aucun (non lisible par JS) | Moyen (à atténuer avec SameSite) | **Préféré** | +| Mémoire (variable JS) | Faible | Aucun | Acceptable pour les SPA | + +Préférerez les cookies `HttpOnly; Secure; SameSite=Strict` pour les applications web. Voir [Mauvaise configuration des cookies HTTP](../vulnerabilities/security-misconfiguration/http-cookies) pour les vérifications associées. + +## Références + +- [OWASP JWT Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html) +- [RFC 7519 — JSON Web Token](https://datatracker.ietf.org/doc/html/rfc7519) +- [Auth0 — JWT Best Practices](https://auth0.com/docs/secure/tokens/json-web-tokens/json-web-token-best-practices) diff --git a/docs/fr/best-practices/security-headers.mdx b/docs/fr/best-practices/security-headers.mdx new file mode 100644 index 00000000..f5e68587 --- /dev/null +++ b/docs/fr/best-practices/security-headers.mdx @@ -0,0 +1,102 @@ +--- +title: En-têtes HTTP de sécurité pour API +description: Découvrez l'importance des en-têtes HTTP de sécurité pour la sécurité des API et comment les tester à l'aide de VulnAPI. +--- + +import { Tabs, TabItem } from '@/components/docs' + +Il est crucial de comprendre l'importance des en-têtes HTTP de sécurité, en particulier en ce qui concerne la sécurité des API. Ces en-têtes fournissent des fonctionnalités de sécurité et des protections importantes à la fois pour le serveur et pour le client. + +## Comment tester ? + +Si vous souhaitez tester uniquement que l'API envoie les bons en-têtes de sécurité, vous pouvez utiliser la commande suivante : + + + +```bash +vulnapi scan curl [url] --scans misconfiguration.http_headers +``` + + +```bash +vulnapi scan openapi [OpenAPI_Path_Or_URL] --scans misconfiguration.http_headers +``` + + +```bash +vulnapi scan graphql --scans misconfiguration.http_headers [url] +``` + + + +## Politiques de sécurité du contenu de l'API (CSP) + +Les en-têtes de politique de sécurité du contenu (CSP) sont essentiels pour atténuer divers risques de sécurité dans le contexte des API. CSP permet de prévenir un ensemble d'attaques, notamment : + +- **Attaques par injection de données :** CSP peut restreindre les types de contenu pouvant être chargés, réduisant ainsi le risque que des attaquants injectent des données malveillantes dans les réponses de l'API. +- **Clickjacking (Détournement de clic) :** Bien qu'il s'agisse principalement d'une préoccupation pour les pages web, CSP peut également aider à atténuer le clickjacking en contrôlant l'encadrement du contenu. + +### Exemple d'en-tête CSP sécurisé pour les API + +```http +Content-Security-Policy: default-src 'none'; frame-ancestors 'none'; +``` + +`frame-ancestors 'none';` garantit que la réponse de l'API ne peut pas être intégrée dans des iframes, empêchant ainsi les attaques de clickjacking car votre API ne devrait probablement pas être intégrée dans des iframes. + +### Risques atténués par CSP + +1. **Script intersite (XSS) :** En limitant les sources à partir desquelles les scripts peuvent être chargés, CSP aide à empêcher les attaquants d'injecter et d'exécuter des scripts malveillants. +2. **Injection de données :** En contrôlant les sources de contenu, CSP réduit le risque que des attaquants injectent des données malveillantes dans les réponses de l'API. +3. **Clickjacking (Détournement de clic) :** En spécifiant frame-ancestors, CSP peut empêcher l'intégration des réponses de l'API dans des iframes, atténuant ainsi les attaques de clickjacking. + +L'implémentation d'en-têtes CSP dans vos réponses d'API est une mesure proactive pour améliorer la posture de sécurité de votre API, protégeant ainsi le serveur et les clients contre divers types d'attaques. + +## Partage de ressources entre origines multiples de l'API (CORS) + +Les en-têtes CORS contrôlent l'accès aux ressources à partir de différentes origines. Pour les API, une configuration CORS appropriée garantit que seuls les domaines de confiance peuvent accéder aux ressources de l'API, empêchant ainsi les accès non autorisés provenant de sites web potentiellement malveillants. + +### Exemple d'en-tête CORS sécurisé pour les API + +```http +Access-Control-Allow-Origin: https://trusted-domain.com +``` + +## Sécurité stricte du transport HTTP (HSTS) + +Les en-têtes HSTS demandent au navigateur de toujours utiliser HTTPS lors de la communication avec le serveur, même si l'utilisateur saisit « http » dans la barre d'adresse. Cela permet d'éviter les attaques de l'homme du milieu et de garantir que toutes les données échangées entre le client et le serveur sont chiffrées. + +### Exemple d'en-tête HSTS sécurisé pour les API + +```http +Strict-Transport-Security: max-age=31536000; includeSubDomains +``` + +## X-Content-Type-Options + +Cet en-tête empêche les navigateurs de procéder à l'analyse MIME (**MIME-sniffing**) d'une réponse pour s'écarter du type de contenu déclaré, ce qui peut aider à prévenir certains types d'attaques, telles que les attaques par confusion de type de contenu. + +### Exemple d'en-tête X-Content-Type-Options sécurisé pour les API + +```http +X-Content-Type-Options: nosniff +``` + +## X-Frame-Options + +L'en-tête X-Frame-Options protège contre les attaques de clickjacking en empêchant la réponse de l'API d'être intégrée dans un frame ou une iframe sur un autre domaine. + +### Exemple d'en-tête X-Frame-Options sécurisé pour les API + +```http +X-Frame-Options: DENY +``` + +## X-Permitted-Cross-Domain-Policies (obsolète) + +Cet en-tête spécifie le fichier de stratégie qui régit la manière dont les applications Flash et Adobe AIR peuvent interagir avec les ressources de l'API à travers différents domaines. Cependant, cet en-tête est obsolète et ne doit pas être utilisé dans les applications modernes. + +## Références + +- [OWASP REST Security Headers](https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html#security-headers) +- [MDN CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) diff --git a/docs/fr/first-scan.mdx b/docs/fr/first-scan.mdx new file mode 100644 index 00000000..53aba434 --- /dev/null +++ b/docs/fr/first-scan.mdx @@ -0,0 +1,83 @@ +--- +title: Votre première analyse +description: Lancez votre première analyse de sécurité d'API en moins d'une minute avec vulnapi scan curl. +sidebar: + order: 4 +--- + +import { Steps, Aside, CardGrid, LinkCard } from '@/components/docs' + +Le moyen le plus rapide d'essayer VulnAPI est d'utiliser `vulnapi scan curl`. Cela fonctionne comme curl — pointez-le vers n'importe quel point de terminaison HTTP et il commencera immédiatement à tester les vulnérabilités. + +## Analyse de base + +```bash +vulnapi scan curl https://api.example.com/endpoint +``` + +C'est tout. VulnAPI envoie une série de requêtes conçues au point de terminaison et affiche un rapport. + +## Analyser un point de terminaison authentifié + +La plupart des API réelles nécessitent une authentification. Transmettez un en-tête `Authorization` de la même manière que vous le feriez avec curl : + +```bash +vulnapi scan curl https://api.example.com/endpoint \ + -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..." +``` + +Vous pouvez répéter `-H` pour des en-têtes supplémentaires (clés d'API, en-têtes personnalisés, etc.) : + +```bash +vulnapi scan curl https://api.example.com/endpoint \ + -H "Authorization: Bearer " \ + -H "X-API-Key: " +``` + +## Analyser un point de terminaison qui n'est pas en GET + +Utilisez `-X` pour définir la méthode HTTP et `-d` pour envoyer un corps de requête : + +```bash +vulnapi scan curl https://api.example.com/login \ + -X POST \ + -d '{"username":"alice","password":"secret"}' \ + -H "Content-Type: application/json" +``` + +## Lire le rapport + +Une fois l'analyse terminée, VulnAPI affiche un tableau comme celui-ci : + +``` +| OPERATION | RISK LEVEL | CVSS 4.0 SCORE | OWASP | VULNERABILITY | +|-----------------------------|------------|----------------|--------------------------|-----------------------------| +| GET /endpoint | High | 9.3 | API2:2023 Broken Auth | JWT Token is not verified | +| GET /endpoint | Medium | 5.1 | API8:2023 Security Misc | X-Frame-Options is missing | +| GET /endpoint | Info | 0.0 | API8:2023 Security Misc | HSTS Header is missing | +``` + +| Colonne | Signification | +|---------|---------------| +| **OPERATION** | La méthode HTTP et le chemin qui ont été testés | +| **RISK LEVEL** | Sévérité : Info / Low / Medium / High / Critical | +| **CVSS 4.0 SCORE** | Score numérique de 0.0 à 10.0 | +| **OWASP** | La catégorie OWASP API Security Top 10 | +| **VULNERABILITY** | Une brève description du résultat | + + + +## Aucun résultat ? + +Une analyse sans aucune ligne dans le tableau des vulnérabilités signifie que VulnAPI n'a rien trouvé avec un score CVSS supérieur au seuil par défaut de `1.0`. C'est un bon signe, mais pas une garantie — VulnAPI recherche un ensemble spécifique de [vulnérabilités connues](./vulnerabilities). + +## Prochaines étapes + + + + + + + diff --git a/docs/fr/getting-started.mdx b/docs/fr/getting-started.mdx new file mode 100644 index 00000000..9700c97f --- /dev/null +++ b/docs/fr/getting-started.mdx @@ -0,0 +1,29 @@ +--- +title: Prise en main +description: Lancez votre première analyse de sécurité d'API en moins de cinq minutes. +sidebar: + order: 2 +--- + +import { CardGrid, LinkCard, Steps } from '@/components/docs' + +Lancez votre première analyse de sécurité d'API en moins de de cinq minutes. + + + +1. **[Installer VulnAPI](./installation)** — Téléchargez un binaire précompilé pour Linux, macOS, Windows, ou exécutez-le via Docker. + +2. **[Lancer votre première analyse](./first-scan)** — Utilisez `vulnapi scan curl` ou pointez VulnAPI vers votre spécification OpenAPI. + +3. **Lire le rapport** — Comprenez les colonnes OPERATION / RISK LEVEL / CVSS / OWASP / VULNERABILITY et ce que signifie chaque résultat. + +4. **[Automatiser dans la CI/CD](./github-action)** — Ajoutez l'action GitHub VulnAPI à votre workflow afin que chaque PR soit analysée automatiquement. + + + + + + + + + diff --git a/docs/fr/github-action.mdx b/docs/fr/github-action.mdx new file mode 100644 index 00000000..1cffa8bf --- /dev/null +++ b/docs/fr/github-action.mdx @@ -0,0 +1,40 @@ +--- +title: GitHub Action +description: VulnAPI peut être intégré à votre pipeline CI/CD à l'aide de GitHub Actions. Cela vous permet d'automatiser les tests de sécurité et le scan de vulnérabilités de vos API dans le cadre de votre flux de développement. +--- + +VulnAPI peut être intégré à votre pipeline CI/CD à l'aide de [GitHub Actions](https://github.com/marketplace/actions/vulnapi-action). Intégrer VulnAPI à GitHub Actions vous permet de scanner vos API à la recherche de vulnérabilités et de risques de sécurité dans le cadre de votre pipeline CI/CD. + +Cela vous permet d'automatiser les tests de sécurité et le scan de vulnérabilités de vos API dans le cadre de votre flux de développement, garantissant que vos API sont **sécurisées** et exemptes de vulnérabilités **avant leur déploiement en production**. + +## Exemple de workflow + +Voici un exemple de workflow GitHub Actions qui utilise VulnAPI pour scanner votre API à la recherche de vulnérabilités : + +```yaml +name: VulnAPI + +on: [push] + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: VulnAPI + uses: cerberauth/vulnapi-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + curl: 'curl http://localhost:8080 -H "Authorization: Bearer eyJhbGci..."' +``` + +## Documentation + +Pour plus d'informations sur l'utilisation de VulnAPI avec GitHub Actions, veuillez consulter la [VulnAPI GitHub Action](https://github.com/marketplace/actions/vulnapi-action) sur GitHub Marketplace. diff --git a/docs/fr/index.mdx b/docs/fr/index.mdx new file mode 100644 index 00000000..e08b8ff8 --- /dev/null +++ b/docs/fr/index.mdx @@ -0,0 +1,124 @@ +--- +title: Introduction +description: VulnAPI est un outil DAST Open Source qui analyse les API à la recherche de vulnérabilités et de risques de sécurité. +sidebar: + order: 1 +--- + +import { Aside } from '@/components/docs' + +Bienvenue dans la documentation de VulnAPI ! + +![Démo](../../demo.gif) + +--- + +## Qu'est-ce que VulnAPI ? + +VulnAPI est un outil DAST Open Source qui analyse les API à la recherche de vulnérabilités et de risques de sécurité. Il fournit des rapports sur toutes les vulnérabilités détectées au cours de l'analyse, incluant le niveau de risque, la vulnérabilité, la description et l'opération effectuée lorsque la vulnérabilité a été détectée. + +VulnAPI propose trois méthodes pour analyser les API : +* **Via l'interface CLI de type Curl** : invoquez directement l'interface de ligne de commande avec des paramètres similaires aux commandes curl. +* **Via les contrats OpenAPI** : utilisez une spécification OpenAPI pour énumérer tous les points de terminaison à analyser. +* **Via GraphQL** : pointez VulnAPI vers un point de terminaison GraphQL pour y rechercher des vulnérabilités. + +## Installation + +Avant d'effectuer votre première analyse avec VulnAPI, vous devez le télécharger et l'installer. Veuillez suivre les instructions sur la page [Installation](./installation). + + + +## Découverte + +Avant de procéder à l'analyse, vous pouvez découvrir des informations utiles sur l'API cible à l'aide de la commande `discover`. + +### Commande Discover + +Pour découvrir des informations utiles sur l'API cible, exécutez la commande suivante : + +```bash +vulnapi discover api [API_URL] +``` + +Exemple de sortie : + +```bash +| WELL-KNOWN PATHS | URL | +|------------------|------------------------------------| +| OpenAPI | http://localhost:5000/openapi.json | +| GraphQL | N/A | + + +| TECHNOLOGIE/SERVICE | VALUE | +|---------------------|---------------| +| Framework | Flask:2.2.3 | +| Language | Python:3.7.17 | +| Server | Flask:2.2.3 | +``` + +### Via l'interface CLI de type Curl + +Pour effectuer une analyse à l'aide de la CLI de type Curl, exécutez la commande suivante : + +```bash +vulnapi scan curl [API_URL] [CURL_OPTIONS] +``` + +Remplacez `[API_URL]` par l'URL de l'API à analyser, et `[CURL_OPTIONS]` par toute option curl supplémentaire que vous souhaitez inclure. + +### Via les contrats OpenAPI + +Pour effectuer une analyse à l'aide de contrats OpenAPI, exécutez la commande suivante : + +```bash +echo "[JWT_TOKEN]" | vulnapi scan openapi [PATH_OR_URL_TO_OPENAPI_FILE] +``` + +Remplacez `[PATH_OR_URL_TO_OPENAPI_FILE]` par le chemin ou l'URL du fichier JSON du contrat OpenAPI et `[JWT_TOKEN]` par le jeton JWT à utiliser pour l'authentification. + +### Via GraphQL + +Pour effectuer une analyse sur un point de terminaison GraphQL, exécutez la commande suivante : + +```bash +vulnapi scan graphql [GRAPHQL_ENDPOINT] -H "Authorization: Bearer [JWT_TOKEN]" +``` + +## Résultats de sortie + +La CLI fournit des rapports détaillés sur toutes les vulnérabilités détectées lors de l'analyse. En voici un exemple condensé : + +| OPERATION | RISK LEVEL | CVSS 4.0 SCORE | OWASP | VULNERABILITY | +|------------------------------|------------|----------------|--------------------------------|--------------------------------| +| GET / | Medium | 5.1 | API8:2023 Security | X-Frame-Options Header is | +| | | | Misconfiguration | missing | +| | High | 9.3 | API2:2023 Broken | JWT Token is not verified | +| | | | Authentication | | +| | Info | 0.0 | API8:2023 Security | HSTS Header is missing | +| | | | Misconfiguration | | + +Consultez la référence des [Formats de sortie](./reference/output-formats) pour obtenir tous les détails sur les colonnes du tableau, les formats JSON/YAML et l'intégration CI/CD. + +## Vulnérabilités détectées + +Toutes les vulnérabilités détectées par le projet sont répertoriées à cette URL : [Vulnérabilités d'API détectées](./vulnerabilities). + +> Davantage de vulnérabilités et de bonnes pratiques seront ajoutées dans les prochaines versions. Si vous avez des suggestions ou des demandes concernant de nouvelles vulnérabilités ou de bonnes pratiques à inclure, n'hésitez pas à ouvrir un ticket (issue) ou à soumettre une pull request. + +## Support proxy + +Le scanner prend en charge les configurations de proxy pour analyser les API situées derrière un serveur proxy. Pour utiliser un proxy, définissez les variables d'environnement `HTTP_PROXY` ou `HTTPS_PROXY` avec l'URL du proxy. + +Un argument de commande `--proxy` est également disponible pour spécifier l'URL du proxy. + +## Options supplémentaires + +Consultez la [Référence de la CLI](./reference/cli) pour obtenir la documentation complète des commandes. + +## Télémétrie + +Le scanner collecte des données d'utilisation anonymes afin de contribuer à l'amélioration de l'outil. Ces données comprennent le nombre d'analyses effectuées, le nombre de vulnérabilités détectées et la sévérité de ces dernières. Aucune information sensible n'est collectée. Vous pouvez désactiver la télémétrie en transmettant le drapeau (flag) `--sqa-opt-out`. + +## Clause de non-responsabilité (Disclaimer) + +Ce scanner est fourni uniquement à des fins éducatives et d'information. Il ne doit pas être utilisé à des fins malveillantes ou pour attaquer un système sans autorisation préalable. Respectez toujours la sécurité et la vie privée d'autrui. diff --git a/docs/fr/installation.mdx b/docs/fr/installation.mdx new file mode 100644 index 00000000..ef960607 --- /dev/null +++ b/docs/fr/installation.mdx @@ -0,0 +1,122 @@ +--- +title: Installation +description: Apprenez à installer VulnAPI. +sidebar: + order: 3 +--- + +Vous pouvez installer les binaires précompilés de VulnAPI sur Linux, Windows et macOS. + +Vous trouverez ci-dessous les instructions pour installer VulnAPI sur Linux, Windows, macOS et Docker. Vous pouvez choisir la méthode d'installation qui correspond le mieux à vos besoins et à votre environnement. + +## Linux + +### Snap + +Vous pouvez installer VulnAPI sur Linux en utilisant [Snap](https://snapcraft.io/vulnapi). + +```bash +sudo snap install vulnapi +``` + +Vérifier : + +```bash +vulnapi --version +``` + +### APT + +Téléchargez la dernière [version de VulnAPI](https://github.com/cerberauth/vulnapi/releases?ref=deb) et installez-la à l'aide de `dpkg`. + +```bash +sudo dpkg -i vulnapi.deb +``` + +Vérifier : + +```bash +vulnapi --version +``` + +### RPM + +Téléchargez la dernière [version de VulnAPI](https://github.com/cerberauth/vulnapi/releases?ref=rpm) et installez-la à l'aide de `rpm -i`. + +```bash +sudo rpm -i vulnapi.rpm +``` + +Vérifier : + +```bash +vulnapi --version +``` + +### Autre + +Vous pouvez installer VulnAPI sur Linux en téléchargeant la dernière [version de VulnAPI](https://github.com/cerberauth/vulnapi/releases?ref=other) et en extrayant le contenu du fichier ZIP. Après l'extraction, exécutez le binaire `vulnapi` depuis la ligne de commande. + +Vérifier : + +```bash +vulnapi --version +``` + +## Windows + +### Chocolatey + +Vous pouvez installer VulnAPI sur Windows en utilisant [Chocolatey](https://chocolatey.org/packages/vulnapi). + +```bash +choco install vulnapi +``` + +Vous pouvez également installer VulnAPI sur Windows en téléchargeant la dernière [version de VulnAPI](https://github.com/cerberauth/vulnapi/releases?ref=windows) et en extrayant le contenu du fichier ZIP. Après l'extraction, vous pouvez exécuter le binaire `vulnapi.exe` depuis la ligne de commande. + +Vérifier : + +```bash +vulnapi --version +``` + +## macOS (Homebrew) + +Vous pouvez installer VulnAPI sur macOS en utilisant Homebrew. Pour ce faire, exécutez la commande suivante : + +```bash +brew tap cerberauth/vulnapi https://github.com/cerberauth/vulnapi +brew install $(brew --repository cerberauth/vulnapi)/vulnapi.rb +``` + +Vérifier : + +```bash +vulnapi --version +``` + +## Docker + +Vous pouvez également utiliser VulnAPI sous forme de conteneur Docker avec [l'image Docker VulnAPI](https://hub.docker.com/r/cerberauth/vulnapi). Pour ce faire, exécutez la commande suivante : + +```bash +docker run --rm cerberauth/vulnapi scan curl [API_URL] [CURL_OPTIONS] +``` + +## GitHub Action + +VulnAPI peut être intégré à votre pipeline CI/CD en utilisant [les GitHub Actions](./github-action). L'intégration de VulnAPI avec les GitHub Actions vous permet d'analyser vos API à la recherche de vulnérabilités et de risques de sécurité dans le cadre de votre pipeline. Cela vous permet d'automatiser les tests de sécurité et l'analyse des vulnérabilités de vos API au sein de votre flux de développement, garantissant que vos API sont **sécurisées** et exemptes de vulnérabilités **avant d'être déployées en production**. + +## Mise à jour + +Gardez VulnAPI à jour pour bénéficier des dernières vérifications de vulnérabilités et corrections de bugs. + +| Plateforme | Commande | +|------------|----------| +| Snap | `sudo snap refresh vulnapi` | +| Chocolatey | `choco upgrade vulnapi` | +| Homebrew | `brew upgrade vulnapi` | +| Docker | `docker pull cerberauth/vulnapi` | + +Pour les installations APT/RPM, téléchargez le package de la dernière version et réinstallez-le en utilisant les mêmes commandes que ci-dessus. diff --git a/docs/fr/labs.mdx b/docs/fr/labs.mdx new file mode 100644 index 00000000..4356e3ba --- /dev/null +++ b/docs/fr/labs.mdx @@ -0,0 +1,20 @@ +--- +title: Labs de vulnérabilités API +description: Labs de vulnérabilités open source pour s'entraîner aux tests de sécurité API avec VulnAPI. +sidebar: + order: 100 +--- + +La liste suivante de labs de vulnérabilités open source offre des ressources aussi bien pour les débutants que pour les experts confirmés en cybersécurité. Ces labs couvrent une grande variété de vulnérabilités API, des plus basiques aux plus complexes, vous permettant d'acquérir une expérience pratique dans l'identification, l'exploitation et la correction des failles de sécurité. Pour l'instant, cette liste n'est pas très longue, mais n'hésitez pas à y contribuer. + +Pour améliorer vos connaissances et compétences en cybersécurité API, nous vous invitons à explorer et à contribuer à cette liste de labs de vulnérabilités : + +| Lab | Documentation de la vulnérabilité | +| ------------------------------------------------------------------------------------------------------------------------ | --------------------------- | +| [Authentication Not Verified](https://github.com/cerberauth/api-vulns-challenges/tree/main/challenges/auth-not-verified) | | +| [JWT Alg None Lab](https://github.com/cerberauth/api-vulns-challenges/tree/main/challenges/jwt-alg-none-bypass) | [Documentation JWT Alg None](./vulnerabilities/broken-authentication/jwt-alg-none) | +| [JWT Blank Secret Lab](https://github.com/cerberauth/api-vulns-challenges/tree/main/challenges/jwt-blank-secret) | [Documentation JWT Blank Secret](./vulnerabilities/broken-authentication/jwt-blank-secret) | +| [JWT Not Verified Lab](https://github.com/cerberauth/api-vulns-challenges/tree/main/challenges/jwt-not-verified) | | +| [JWT Null Signature Lab](https://github.com/cerberauth/api-vulns-challenges/tree/main/challenges/jwt-null-signature) | [Documentation JWT Null Signature](./vulnerabilities/broken-authentication/jwt-null-signature) | +| [JWT Weak HMAC Secret Lab](https://github.com/cerberauth/api-vulns-challenges/tree/main/challenges/jwt-weak-hmac-secret) | [Documentation JWT Weak HMAC Secret](./vulnerabilities/broken-authentication/jwt-weak-secret) | +| [JWT Weak RSA Key Lab](https://github.com/cerberauth/api-vulns-challenges/tree/main/challenges/jwt-weak-rsa-key) | | diff --git a/docs/fr/reference/_meta.yml b/docs/fr/reference/_meta.yml new file mode 100644 index 00000000..87705563 --- /dev/null +++ b/docs/fr/reference/_meta.yml @@ -0,0 +1 @@ +label: Référence diff --git a/docs/fr/reference/cli/_meta.yml b/docs/fr/reference/cli/_meta.yml new file mode 100644 index 00000000..cda59476 --- /dev/null +++ b/docs/fr/reference/cli/_meta.yml @@ -0,0 +1 @@ +label: CLI diff --git a/docs/fr/reference/cli/discover-api.mdx b/docs/fr/reference/cli/discover-api.mdx new file mode 100644 index 00000000..e93efab6 --- /dev/null +++ b/docs/fr/reference/cli/discover-api.mdx @@ -0,0 +1,41 @@ +--- +title: discover api +description: Découvrir les chemins bien connus et les empreintes technologiques d'une API. +--- + +Sonde une URL de base à la recherche de chemins API courants (ex. `/openapi.json`, `/graphql`, `/.well-known/`) et lit les en-têtes de réponse pour identifier le framework et le langage du serveur. + +## Usage + +```sh +vulnapi discover api [flags] +``` + +Accepte toutes les [options communes](./#common-flags). + +## Sortie + +La commande affiche deux tableaux : + +- **Well-known paths** — chemins ayant renvoyé une réponse différente de 404, avec leurs codes de statut HTTP. +- **Fingerprints** — technologies détectées à partir des en-têtes de réponse (serveur, framework, langage, etc.). + +## Exemples + +**Découvrir les chemins bien connus et les empreintes** +```sh +vulnapi discover api https://api.example.com +``` + +**Passer un en-tête Authorization** +```sh +vulnapi discover api https://api.example.com \ + -H "Authorization: Bearer " +``` + +## Codes de sortie + +| Code | Signification | +|---|---| +| `0` | Découverte terminée avec succès | +| `1` | Erreur fatale (URL invalide, échec réseau, etc.) | diff --git a/docs/fr/reference/cli/discover-domain.mdx b/docs/fr/reference/cli/discover-domain.mdx new file mode 100644 index 00000000..31b15999 --- /dev/null +++ b/docs/fr/reference/cli/discover-domain.mdx @@ -0,0 +1,33 @@ +--- +title: discover domain +description: Découvrir des endpoints API à travers des sous-domaines courants d'un domaine. +--- + +Énumère les sous-domaines courants d'un domaine, effectue des résolutions DNS inverses, et exécute les contrôles [discover api](./discover-api) sur chaque hôte découvert. + +## Usage + +```sh +vulnapi discover domain [flags] +``` + +Accepte toutes les [options communes](./#common-flags). + +## Exemples + +**Découvrir des API sur tous les sous-domaines** +```sh +vulnapi discover domain example.com +``` + +**Limiter le débit des requêtes** +```sh +vulnapi discover domain example.com --rate 5/s +``` + +## Codes de sortie + +| Code | Signification | +|---|---| +| `0` | Découverte terminée avec succès | +| `1` | Erreur fatale (domaine invalide, échec réseau, etc.) | diff --git a/docs/fr/reference/cli/index.mdx b/docs/fr/reference/cli/index.mdx new file mode 100644 index 00000000..bc1e787f --- /dev/null +++ b/docs/fr/reference/cli/index.mdx @@ -0,0 +1,65 @@ +--- +title: Référence CLI +description: Vue d'ensemble de toutes les commandes CLI de VulnAPI. +sidebar: + order: 1 +--- + +import { LinkCard, CardGrid } from '@/components/docs'; + +Le CLI `vulnapi` couvre le scan de sécurité API, la découverte, et les utilitaires JWT. + + + + + + + + + + + +## Usage global + +```sh +vulnapi [flags] +vulnapi --help +vulnapi version +``` + +Toutes les commandes retournent `0` en cas de succès et une valeur non nulle en cas d'erreur. + +## Options globales + +| Option | Description | +|---|---| +| `--sqa-opt-out` | Se désinscrire des statistiques d'usage anonymes et des rapports de crash | +| `-h, --help` | Afficher l'aide pour n'importe quelle commande | + +## Options communes + +Toutes les commandes `scan` et `discover` partagent ces options. Les options du client HTTP suivent les mêmes conventions que `curl`. + +| Option | Par défaut | Description | +|---|---|---| +| `--rate ` | `` | Limite de débit des requêtes (ex. `10/s`, `1/m`) | +| `-x, --proxy ` | `` | URL de proxy pour toutes les requêtes | +| `-H, --header
` | `` | En-tête à inclure dans les requêtes (répétable) | +| `-b, --cookie ` | `` | Cookie à inclure dans les requêtes (répétable) | +| `-u, --user ` | `` | Nom d'utilisateur et mot de passe pour l'authentification HTTP Basic | +| `--basic` | `` | Utiliser l'authentification HTTP Basic (avec `-u`) | +| `--digest` | `` | Utiliser l'authentification HTTP Digest (avec `-u`) | +| `--scans ` | `` | Exécuter uniquement des scans spécifiques (répétable ; supporte les jokers comme `jwt.*`) | +| `--exclude-scans ` | `` | Exclure des scans spécifiques (répétable) | +| `--report-format ` | `table` | Format de sortie : `table`, `json`, `yaml` | +| `--report-transport ` | `file` | Transport du rapport : `file`, `http` | +| `--report-file ` | `` | Chemin du fichier où écrire le rapport | +| `--report-url ` | `` | URL vers laquelle envoyer le rapport (avec `--report-transport http`) | +| `--no-progress` | `false` | Désactiver la barre de progression | +| `--severity-threshold ` | `1.0` | Seuil CVSS au-dessus duquel la sortie est aussi écrite sur stderr | + +> **Déprécié :** `--rate-limit` (`-r`) a été remplacé par `--rate`. L'ancienne option est toujours acceptée en tant qu'alias. + +## Comportement de sortie + +Lorsqu'au moins un résultat a un score CVSS supérieur à `--severity-threshold`, VulnAPI écrit la sortie sur **stderr** en plus de stdout. Cela déclenche automatiquement les patterns `|| exit 1` et les contrôles d'échec en CI/CD. Le seuil par défaut est `1.0`, ce qui signifie que tout résultat avec un CVSS > 1.0 déclenche une sortie sur stderr. diff --git a/docs/fr/reference/cli/jwt-generate.mdx b/docs/fr/reference/cli/jwt-generate.mdx new file mode 100644 index 00000000..0a2a3f86 --- /dev/null +++ b/docs/fr/reference/cli/jwt-generate.mdx @@ -0,0 +1,48 @@ +--- +title: jwt generate +description: Générer un nouveau JWT à partir d'un token existant, re-signé avec un algorithme ou une clé différente. +--- + +Analyse un JWT existant et le re-signe avec un nouvel algorithme et une nouvelle clé. Les claims d'origine sont préservés. Utile pour fabriquer manuellement des tokens afin de tester les mitigations de vulnérabilités JWT. + +## Usage + +```sh +vulnapi jwt generate --alg --secret +``` + +## Options + +| Option | Description | +|---|---| +| `--alg ` | Algorithme de signature **(requis)** : `HS256`, `HS384`, `HS512`, `RS256`, `RS384`, `RS512`, `ES256`, `ES384`, `NONE` | +| `--secret ` | Clé secrète pour signer le token (requise pour les algorithmes HMAC) | +| `--aud ` | Claim d'audience (`aud`) à définir dans le token | + +## Exemples + +**Re-signer avec un secret HMAC différent** +```sh +vulnapi jwt generate eyJhbGciOiJIUzI1NiI... --alg HS256 --secret newsecret +``` + +**Retirer la signature (alg=none)** +```sh +vulnapi jwt generate eyJhbGciOiJIUzI1NiI... --alg NONE +``` + +**Re-signer avec une audience personnalisée** +```sh +vulnapi jwt generate eyJhbGciOiJIUzI1NiI... --alg HS256 --secret mysecret --aud myservice +``` + +## Sortie + +La chaîne du token signé est affichée sur stdout. + +## Codes de sortie + +| Code | Signification | +|---|---| +| `0` | Token généré avec succès | +| `1` | Token d'entrée invalide, algorithme inconnu, ou options requises manquantes | diff --git a/docs/fr/reference/cli/scan-curl.mdx b/docs/fr/reference/cli/scan-curl.mdx new file mode 100644 index 00000000..dc6db578 --- /dev/null +++ b/docs/fr/reference/cli/scan-curl.mdx @@ -0,0 +1,59 @@ +--- +title: scan curl +description: Scanner un unique endpoint API avec des options de type curl. +--- + +Scanne un unique endpoint API en spécifiant une URL et, optionnellement, une méthode HTTP et un corps de requête. Accepte les mêmes options que `curl` pour faciliter le copier-coller de commandes depuis la documentation ou les DevTools du navigateur. + +## Usage + +```sh +vulnapi scan curl [flags] +``` + +## Options + +| Option | Par défaut | Description | +|---|---|---| +| `-X, --request ` | `GET` | Méthode HTTP à utiliser | +| `-d, --data ` | `` | Corps de la requête HTTP | + +Accepte aussi toutes les [options communes](./#common-flags). + +## Exemples + +**Simple requête GET** +```sh +vulnapi scan curl https://api.example.com/endpoint +``` + +**POST avec un corps JSON et un en-tête Authorization** +```sh +vulnapi scan curl https://api.example.com/endpoint \ + -X POST \ + -d '{"key":"value"}' \ + -H "Authorization: Bearer " +``` + +**Exécuter uniquement les scans liés au JWT** +```sh +vulnapi scan curl https://api.example.com/endpoint \ + -H "Authorization: Bearer " \ + --scans "jwt.*" +``` + +**Écrire le rapport en JSON dans un fichier** +```sh +vulnapi scan curl https://api.example.com/endpoint \ + --report-format json \ + --report-file report.json +``` + +## Codes de sortie + +| Code | Signification | +|---|---| +| `0` | Scan terminé avec succès | +| `1` | Erreur fatale (URL invalide, échec réseau, etc.) | + +Quand un résultat dépasse `--severity-threshold`, une sortie supplémentaire est écrite sur stderr. Voir [comportement de sortie](./#exit-behaviour). diff --git a/docs/fr/reference/cli/scan-graphql.mdx b/docs/fr/reference/cli/scan-graphql.mdx new file mode 100644 index 00000000..fa896603 --- /dev/null +++ b/docs/fr/reference/cli/scan-graphql.mdx @@ -0,0 +1,45 @@ +--- +title: scan graphql +description: Scanner un endpoint GraphQL à la recherche de vulnérabilités de sécurité. +--- + +Exécute des scans de sécurité sur un endpoint GraphQL, incluant des contrôles pour l'exposition de l'introspection et d'autres vulnérabilités spécifiques à GraphQL. + +## Usage + +```sh +vulnapi scan graphql [flags] +``` + +Accepte toutes les [options communes](./#common-flags). + +## Exemples + +**Scanner un endpoint GraphQL avec un bearer token** +```sh +vulnapi scan graphql https://api.example.com/graphql \ + -H "Authorization: Bearer " +``` + +**Scanner via un proxy** +```sh +vulnapi scan graphql https://api.example.com/graphql \ + -H "Authorization: Bearer " \ + --proxy http://localhost:8080 +``` + +**Exporter les résultats en JSON** +```sh +vulnapi scan graphql https://api.example.com/graphql \ + --report-format json \ + --report-file report.json +``` + +## Codes de sortie + +| Code | Signification | +|---|---| +| `0` | Scan terminé avec succès | +| `1` | Erreur fatale (URL invalide, échec réseau, etc.) | + +Quand un résultat dépasse `--severity-threshold`, une sortie supplémentaire est écrite sur stderr. Voir [comportement de sortie](./#exit-behaviour). diff --git a/docs/fr/reference/cli/scan-openapi.mdx b/docs/fr/reference/cli/scan-openapi.mdx new file mode 100644 index 00000000..157abcdd --- /dev/null +++ b/docs/fr/reference/cli/scan-openapi.mdx @@ -0,0 +1,61 @@ +--- +title: scan openapi +description: Scanner tous les endpoints définis dans une spécification OpenAPI. +--- + +Charge une spécification OpenAPI (fichier local ou URL distante) et exécute des scans de sécurité sur chaque opération qu'elle définit. + +## Usage + +```sh +vulnapi scan openapi [flags] +``` + +## Options + +| Option | Par défaut | Description | +|---|---|---| +| `--security-schemes ` | `` | Remplace la valeur du token pour un schéma de sécurité nommé (répétable) | + +Accepte aussi toutes les [options communes](./#common-flags). + +## Token via stdin + +Un token transmis via stdin est utilisé comme valeur par défaut pour tous les schémas de sécurité acceptant un bearer token. Les surcharges nommées via `--security-schemes` sont prioritaires. + +```sh +echo "" | vulnapi scan openapi ./openapi.json +``` + +## Exemples + +**Spécification locale avec un token transmis via stdin** +```sh +echo "" | vulnapi scan openapi ./openapi.json +``` + +**Spécification distante avec un token transmis via stdin** +```sh +echo "" | vulnapi scan openapi https://api.example.com/.well-known/openapi.json +``` + +**Surcharge d'un schéma de sécurité nommé** +```sh +vulnapi scan openapi ./openapi.json --security-schemes bearerAuth= +``` + +**Plusieurs surcharges de schémas** +```sh +vulnapi scan openapi ./openapi.json \ + --security-schemes bearerAuth= \ + --security-schemes apiKey= +``` + +## Codes de sortie + +| Code | Signification | +|---|---| +| `0` | Scan terminé avec succès | +| `1` | Erreur fatale (spécification invalide, échec réseau, etc.) | + +Quand un résultat dépasse `--severity-threshold`, une sortie supplémentaire est écrite sur stderr. Voir [comportement de sortie](./#exit-behaviour). diff --git a/docs/fr/reference/cli/serve.mdx b/docs/fr/reference/cli/serve.mdx new file mode 100644 index 00000000..2374c356 --- /dev/null +++ b/docs/fr/reference/cli/serve.mdx @@ -0,0 +1,37 @@ +--- +title: serve +description: Démarrer le serveur HTTP VulnAPI en mode API REST. +--- + +Démarre VulnAPI en tant que serveur HTTP exposant une API REST, permettant de déclencher des scans par programmation plutôt que via le CLI. + +## Usage + +```sh +vulnapi serve [flags] +``` + +## Options + +| Option | Par défaut | Description | +|---|---|---| +| `-p, --port ` | `8080` | Port d'écoute | + +## Exemples + +**Démarrer sur le port par défaut** +```sh +vulnapi serve +``` + +**Démarrer sur un port personnalisé** +```sh +vulnapi serve --port 9090 +``` + +## Codes de sortie + +| Code | Signification | +|---|---| +| `0` | Le serveur s'est arrêté proprement | +| `1` | Erreur fatale (port déjà utilisé, etc.) | diff --git a/docs/fr/reference/output-formats.mdx b/docs/fr/reference/output-formats.mdx new file mode 100644 index 00000000..84b313ea --- /dev/null +++ b/docs/fr/reference/output-formats.mdx @@ -0,0 +1,93 @@ +--- +title: Formats de sortie +description: Référence des formats de sortie table, JSON et YAML de VulnAPI, et intégration du seuil de gravité en CI/CD. +--- + +VulnAPI prend en charge trois formats de sortie : `table` (par défaut), `json` et `yaml`. Utilisez `--report-format` pour choisir le format et `--report-transport` + `--report-file` / `--report-url` pour contrôler la destination de la sortie. + +## Table (par défaut) + +Le format table affiche des tableaux ASCII lisibles dans le terminal. Il comporte jusqu'à trois sous-tableaux : + +**Well-Known Paths** — spécification API et endpoints GraphQL découverts : + +``` +| WELL-KNOWN PATHS | URL | +|------------------|------------------------------------| +| OpenAPI | http://localhost:5000/openapi.json | +| GraphQL | N/A | +``` + +**Technology Fingerprint** — framework, langage et serveur détectés à partir des en-têtes de réponse : + +``` +| TECHNOLOGIE/SERVICE | VALUE | +|---------------------|---------------| +| Framework | Flask:2.2.3 | +| Language | Python:3.11.9 | +| Server | Flask:2.2.3 | +``` + +**Vulnerability List** — chaque résultat sur sa propre ligne : + +| Colonne | Description | +|--------|-------------| +| **OPERATION** | Méthode HTTP + chemin testé (ex. `GET /api/users`) | +| **RISK LEVEL** | Gravité lisible : Info / Low / Medium / High / Critical | +| **CVSS 4.0 SCORE** | Score numérique 0.0–10.0 | +| **OWASP** | Catégorie OWASP API Security Top 10 concernée | +| **VULNERABILITY** | Courte description du résultat | + +Une ligne de conseil au-dessus du tableau résume le niveau de risque global. + +## JSON + +Exportez un rapport JSON structuré vers un fichier : + +```bash +vulnapi scan curl https://api.example.com/endpoint \ + -H "Authorization: Bearer " \ + --report-format json \ + --report-transport file \ + --report-file report.json +``` + +La sortie JSON contient un tableau de résultats de scan, chacun avec les détails de l'opération, les métadonnées du problème, le score CVSS, la classification OWASP, et les réponses des tentatives de scan. Utilisez ce format pour un traitement automatisé, des tableaux de bord, ou le stockage d'artefacts en CI/CD. + +## YAML + +```bash +vulnapi scan curl https://api.example.com/endpoint \ + --report-format yaml \ + --report-transport file \ + --report-file report.yaml +``` + +Le format YAML contient les mêmes données que le format JSON, en syntaxe YAML. + +## Seuil de gravité et intégration CI/CD + +L'option `--severity-threshold` (par défaut `1.0`) contrôle quand VulnAPI écrit les résultats sur **stderr**. Si un résultat a un score CVSS strictement supérieur au seuil, la sortie est aussi écrite sur stderr. + +Cela permet de détecter l'échec d'un pipeline CI/CD sans script intermédiaire : + +```bash +# Faire échouer le pipeline si un résultat a un CVSS > 5.0 +vulnapi scan curl https://api.example.com/endpoint \ + -H "Authorization: Bearer " \ + --severity-threshold 5.0 \ + 2>/dev/null || exit 1 +``` + +```yaml +# Exemple GitHub Actions +- name: VulnAPI Scan + run: | + vulnapi scan openapi ./openapi.json \ + --severity-threshold 7.0 \ + --report-format json \ + --report-file vulnapi-report.json + # L'étape échoue automatiquement si un CVSS > 7.0 est détecté (sortie stderr) +``` + +Définissez `--severity-threshold 0` pour toujours écrire sur stderr, quel que soit le score CVSS. diff --git a/docs/fr/reference/scan-ids.mdx b/docs/fr/reference/scan-ids.mdx new file mode 100644 index 00000000..0a9d2e87 --- /dev/null +++ b/docs/fr/reference/scan-ids.mdx @@ -0,0 +1,80 @@ +--- +title: Référence des identifiants de scan +description: Tableau de référence complet de tous les identifiants de scan VulnAPI, catégories et scores de gravité. +--- + +Utilisez les identifiants de scan avec les options `--scans` et `--exclude-scans` pour filtrer les contrôles exécutés par VulnAPI. + +```bash +# Exécuter uniquement les contrôles JWT +vulnapi scan curl https://api.example.com -H "Authorization: Bearer " --scans jwt.alg_none + +# Exécuter tous les contrôles JWT avec un joker +vulnapi scan curl ... --scans "jwt.*" + +# Exclure tous les contrôles de découverte +vulnapi scan curl ... --exclude-scans "discover.*" +``` + +## Broken Authentication + +| Identifiant de scan | Nom | OWASP | CVSS | +|---------|------|-------|------| +| `jwt.alg_none` | JWT accepte `alg: none` | API2:2023 | Élevé | +| `jwt.blank_secret` | JWT signé avec un secret vide | API2:2023 | Élevé | +| `jwt.weak_secret` | JWT signé avec un secret faible | API2:2023 | Élevé | +| `jwt.kid_injection` | Injection d'en-tête JWT KID (injection SQL et traversée de chemin) | API2:2023 | 9.3 | +| `jwt.not_verified` | Signature JWT non vérifiée | API2:2023 | 9.3 | +| `jwt.null_signature` | Signature JWT nulle | API2:2023 | Élevé | +| `generic.accept_unauthenticated_operation` | Le endpoint accepte les requêtes non authentifiées | API2:2023 | 9.3 | + +## Security Misconfiguration + +| Identifiant de scan | Nom | OWASP | CVSS | +|---------|------|-------|------| +| `misconfiguration.http_headers` | En-têtes de réponse de sécurité manquants | API8:2023 | Info–Moyen | +| `misconfiguration.http_cookies` | Indicateurs de sécurité des cookies mal configurés | API8:2023 | 0.0 | +| `misconfiguration.http_trace` | Méthode HTTP TRACE activée | API8:2023 | Info | +| `misconfiguration.http_track` | Méthode HTTP TRACK activée | API8:2023 | Info | +| `misconfiguration.http_method_override` | En-têtes de substitution de méthode HTTP acceptés | API8:2023 | Info–Élevé | + +## GraphQL + +| Identifiant de scan | Nom | OWASP | CVSS | +|---------|------|-------|------| +| `graphql.introspection_enabled` | Introspection GraphQL activée | API8:2023 | Faible | + +## Discovery + +Les scans de découverte s'exécutent automatiquement avec les commandes `vulnapi discover`. Ils peuvent aussi être inclus/exclus sur les commandes de scan. + +| Identifiant de scan | Nom | Description | +|---------|------|-------------| +| `discover.api` | Empreinte API | Détecte le framework, le langage et le serveur à partir des en-têtes de réponse | +| `discover.well_known_paths` | Découverte de chemins bien connus | Vérifie la présence d'OpenAPI, GraphQL et autres chemins courants | +| `discover.domain` | Découverte de domaine | Motifs de sous-domaines courants et résolution DNS inverse | + +## En-têtes de sécurité HTTP (sous-contrôles de `misconfiguration.http_headers`) + +Le scan `misconfiguration.http_headers` couvre plusieurs contrôles d'en-têtes individuels. Vous pouvez exclure l'identifiant de scan parent pour tous les ignorer. + +| En-tête | Risque | +|--------|------| +| `Content-Security-Policy` | Moyen | +| `X-Frame-Options` | Moyen | +| `X-Content-Type-Options` | Info | +| `Strict-Transport-Security` | Info | +| `Access-Control-Allow-Origin` (CORS) | Moyen | +| Fuite de signature du serveur | Info | + +## Sécurité des cookies (sous-contrôles de `misconfiguration.http_cookies`) + +Le scan `misconfiguration.http_cookies` couvre cinq contrôles individuels d'indicateurs de cookies : + +| Identifiant de contrôle | Description | CWE | +|----------|-------------|-----| +| `security_misconfiguration.http_cookies_not_http_only` | Cookie accessible via JavaScript (risque XSS) | CWE-1004 | +| `security_misconfiguration.http_cookies_not_secure` | Cookie transmis en HTTP | CWE-614 | +| `security_misconfiguration.http_cookies_same_site_none` | `SameSite=None` autorise les requêtes cross-site | CWE-1275 | +| `security_misconfiguration.http_cookies_without_same_site` | Attribut `SameSite` non défini | CWE-1275 | +| `security_misconfiguration.http_cookies_without_expires` | Le cookie de session n'expire jamais | CWE-613 | diff --git a/docs/fr/vampi.mdx b/docs/fr/vampi.mdx new file mode 100644 index 00000000..170d4769 --- /dev/null +++ b/docs/fr/vampi.mdx @@ -0,0 +1,89 @@ +--- +title: Entraînement VulnAPI sur VAmPI (API REST vulnérable) +description: Entraînez-vous à utiliser VulnAPI pour scanner des API REST à la recherche de vulnérabilités et de risques de sécurité. +sidebar: + order: 5 +--- + +VulnAPI est un outil puissant qui vous permet de scanner des API REST à la recherche de vulnérabilités et de risques de sécurité. Ce module d'entraînement vous guidera à travers le processus d'utilisation de VulnAPI pour scanner vos API à la recherche de vulnérabilités et de risques de sécurité. + +VAmPI est une API REST vulnérable comportant des vulnérabilités du OWASP Top 10. Vous pouvez utiliser VAmPI pour vous entraîner à scanner des API REST à la recherche de vulnérabilités et de risques de sécurité avec VulnAPI. + +## Prérequis + +Avant de commencer ce module d'entraînement, vous devez disposer des prérequis suivants : + +- [VulnAPI](./installation) installé sur votre machine. +- [VAmPI](https://github.com/erev0s/VAmPI) installé sur votre machine. + +## Objectifs de l'entraînement + +À la fin de ce module d'entraînement, vous serez capable de : + +- Scanner une API REST à la recherche de vulnérabilités et de risques de sécurité avec VulnAPI. +- Identifier et corriger les vulnérabilités et risques de sécurité d'une API REST. + +## Étapes de l'entraînement + +Suivez les étapes ci-dessous pour terminer ce module d'entraînement : + +1. Démarrez le serveur VAmPI en exécutant la commande suivante : + + ```bash + docker run -e vulnerable=1 -p 5000:5000 erev0s/vampi + ``` + +2. Ouvrez une nouvelle fenêtre de terminal et exécutez la commande suivante pour effectuer un scan de découverte de l'API VAmPI avec VulnAPI : + + ```bash + vulnapi scan discover http://localhost:5000 + ``` + +3. Consultez les résultats du scan pour identifier le contrat OpenAPI et les informations de service supplémentaires. +4. Téléchargez le contrat OpenAPI et enregistrez-le dans un fichier nommé `vampi-openapi.json`. Modifiez le fichier, ajoutez une URL de base au serveur ainsi qu'un schéma de sécurité. + + La sécurité doit être ajoutée pour les endpoints `PUT /users/v1/{username}/email`, `PUT /users/v1/{username}/password` et `DELETE /users/v1/{username}` avec le schéma de sécurité `bearerAuth`. + + ```json + { + "components": { + "securitySchemes": { + "bearerAuth": { + "bearerFormat": "JWT", + "scheme": "bearer", + "type": "http" + } + } + }, + "info": { + "description": "OpenAPI v3 specs for VAmPI", + "title": "VAmPI", + "version": "0.1" + }, + "openapi": "3.0.1", + "paths": { + "/users/v1/{username}/email": { + "put": { + "description": "Update a single users email", + "operationId": "api_views.users.update_email", + "security": [ + { + "bearerAuth": [] + } + ], + ... + "servers": [ + { + "url": "http://localhost:5000" + } + ] + } + ``` + +5. Exécutez la commande suivante pour scanner l'API VAmPI à l'aide du contrat OpenAPI : + + ```bash + vulnapi scan openapi vampi-openapi.json + ``` + +6. Consultez les résultats du scan pour identifier les vulnérabilités et risques de sécurité de l'API VAmPI. diff --git a/docs/fr/vulnerabilities.mdx b/docs/fr/vulnerabilities.mdx new file mode 100644 index 00000000..ba07292f --- /dev/null +++ b/docs/fr/vulnerabilities.mdx @@ -0,0 +1,48 @@ +--- +title: Vulnérabilités API +description: Liste de toutes les vulnérabilités détectées par VulnAPI. +sidebar: + order: 100 +--- + +VulnAPI détecte un large éventail de vulnérabilités API, y compris celles du OWASP API Security Top 10. + +✅ = scan automatisé disponible + +| Nom | OWASP | Gravité | Scan Automatisé | +|-------------------------------------------------------------------------------------------------------|---------------------------------------------|:--------:|:--------------:| +| Rupture d'autorisation au niveau de l'objet (BOLA) | API1:2023 Broken Object Level Authorization | Moyenne | | +| Accès aux champs privés | API1:2023 Broken Object Level Authorization | Moyenne | | +| Affectation de masse | API1:2023 Broken Object Level Authorization | Moyenne | | +| [Contournement de l'authentification](./vulnerabilities/broken-authentication/authentication-bypass) | API2:2023 Broken Authentication | Élevée | ✅ | +| [Algorithme JWT `none`](./vulnerabilities/broken-authentication/jwt-alg-none) | API2:2023 Broken Authentication | Élevée | ✅ | +| [Secret JWT vide](./vulnerabilities/broken-authentication/jwt-blank-secret) | API2:2023 Broken Authentication | Élevée | ✅ | +| [Secret JWT faible](./vulnerabilities/broken-authentication/jwt-weak-secret) | API2:2023 Broken Authentication | Élevée | ✅ | +| [Attaque de relais inter-services via l'audience JWT](./vulnerabilities/broken-authentication/jwt-cross-service-relay-attack) | API2:2023 Broken Authentication | Élevée | | +| [Signature JWT nulle](./vulnerabilities/broken-authentication/jwt-null-signature) | API2:2023 Broken Authentication | Élevée | ✅ | +| [Injection d'en-tête JWT KID](./vulnerabilities/broken-authentication/jwt-kid-injection) | API2:2023 Broken Authentication | Élevée | ✅ | +| [Confusion d'algorithme JWT](./vulnerabilities/broken-authentication/jwt-algorithm-confusion) | API2:2023 Broken Authentication | Élevée | ✅ | +| [Signature JWT non vérifiée](./vulnerabilities/broken-authentication/jwt-not-verified) | API2:2023 Broken Authentication | Élevée | ✅ | +| [JWT expiré](./vulnerabilities/broken-authentication/jwt-expired) | API2:2023 Broken Authentication | Élevée | | +| OpenAPI détectable | API7:2023 Server Side Request Forgery | Info | ✅ | +| Point de terminaison GraphQL détectable | API7:2023 Server Side Request Forgery | Info | ✅ | +| [Introspection GraphQL activée](./vulnerabilities/security-misconfiguration/graphql-introspection) | API8:2023 Security Misconfiguration | Info | ✅ | +| Fuite de secrets | API8:2023 Security Misconfiguration | Élevée | | +| Listage de répertoires | API8:2023 Security Misconfiguration | Moyenne | | +| Divulgation d'IP privée | API8:2023 Security Misconfiguration | Faible | | +| [Mauvaise configuration des cookies HTTP](./vulnerabilities/security-misconfiguration/http-cookies) | API8:2023 Security Misconfiguration | Info | ✅ | +| Absence d'en-têtes CORS | API8:2023 Security Misconfiguration | Info | ✅ | +| En-têtes CORS permissifs | API8:2023 Security Misconfiguration | Info | ✅ | +| [Substitution de méthode HTTP activée](./vulnerabilities/security-misconfiguration/http-method-allow-override) | API8:2023 Security Misconfiguration | Info - Élevée | ✅ | +| En-tête X-Content-Type-Options non défini | API8:2023 Security Misconfiguration | Info | ✅ | +| En-tête X-Frame-Options non défini | API8:2023 Security Misconfiguration | Info | ✅ | +| En-tête CSP non défini | API8:2023 Security Misconfiguration | Info | ✅ | +| Directive CSP frame-ancestors non définie | API8:2023 Security Misconfiguration | Info | ✅ | +| En-tête HSTS non défini | API8:2023 Security Misconfiguration | Info | ✅ | +| [Méthodes HTTP TRACE/TRACK activées](./vulnerabilities/security-misconfiguration/http-trace-track) | API8:2023 Security Misconfiguration | Info | ✅ | +| Fuite de signature du serveur | API8:2023 Security Misconfiguration | Info | ✅ | +| Certificat SSL non fiable | API8:2023 Security Misconfiguration | Moyenne | | +| SSL non imposé | API8:2023 Security Misconfiguration | Moyenne | | +| Traversée de répertoires | API10:2023 Unsafe Consumption of APIs | Élevée | | + +Les scans de vulnérabilités JWT sont en partie propulsés par le projet [CerberAuth JWTop](https://github.com/cerberauth/jwtop). diff --git a/docs/fr/vulnerabilities/_meta.yml b/docs/fr/vulnerabilities/_meta.yml new file mode 100644 index 00000000..57402698 --- /dev/null +++ b/docs/fr/vulnerabilities/_meta.yml @@ -0,0 +1 @@ +label: Vulnérabilités diff --git a/docs/fr/vulnerabilities/broken-authentication/_meta.yml b/docs/fr/vulnerabilities/broken-authentication/_meta.yml new file mode 100644 index 00000000..d4543fab --- /dev/null +++ b/docs/fr/vulnerabilities/broken-authentication/_meta.yml @@ -0,0 +1 @@ +label: Authentification défaillante diff --git a/docs/fr/vulnerabilities/broken-authentication/authentication-bypass.mdx b/docs/fr/vulnerabilities/broken-authentication/authentication-bypass.mdx new file mode 100644 index 00000000..4586cdc2 --- /dev/null +++ b/docs/fr/vulnerabilities/broken-authentication/authentication-bypass.mdx @@ -0,0 +1,103 @@ +--- +title: Contournement de l'authentification +description: Une vulnérabilité où un endpoint nécessitant une authentification accepte des requêtes sans identifiants, permettant un accès non authentifié. +--- + +import { Tabs, TabItem } from '@/components/docs' + + + + + + + + + + + + + + + + + + +
Identifiant de scangeneric.accept_unauthenticated_operation
GravitéÉlevée
CVSS 4.09.3 — CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N
Catégorie OWASP + OWASP API2:2023 Broken Authentication +
+ +La vulnérabilité « Contournement de l'authentification » survient lorsqu'un endpoint censé nécessiter une authentification renvoie une réponse de succès (2xx) alors que la requête ne contient aucun identifiant. VulnAPI la détecte en envoyant une requête au endpoint sans en-tête `Authorization`, cookies, ou autres identifiants, et en vérifiant si la réponse indique un succès plutôt que de renvoyer `401 Unauthorized` ou `403 Forbidden`. + +## Exemple + +**Requête authentifiée normale** (devrait réussir) : +```http +GET /api/users/profile HTTP/1.1 +Authorization: Bearer eyJhbGciOiJSUzI1NiI... + +HTTP/1.1 200 OK +``` + +**Requête non authentifiée** (devrait être rejetée — mais est vulnérable si elle renvoie 200) : +```http +GET /api/users/profile HTTP/1.1 + +HTTP/1.1 200 OK ← vulnérable ! +``` + +## Comment tester ? + + + +```bash +vulnapi scan curl [url] -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans generic.accept_unauthenticated_operation +``` + + +```bash +echo "eyJhbGciOiJSUzUxMiI..." | vulnapi scan openapi [OpenAPI_Path_Or_URL] --scans generic.accept_unauthenticated_operation +``` + + +```bash +vulnapi scan graphql -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans generic.accept_unauthenticated_operation [url] +``` + + + +VulnAPI prend aussi en charge le scan d'autres types de [vulnérabilités](../). + +## Quel est l'impact ? + +Si un attaquant peut accéder à un endpoint protégé sans aucun identifiant, il peut : + +- **Accéder à des données sensibles** : lire des profils utilisateur, des dossiers financiers, des messages privés, ou toute autre donnée renvoyée par le endpoint +- **Effectuer des opérations privilégiées** : créer, modifier, ou supprimer des ressources sans être connecté +- **Contourner entièrement la couche d'authentification** : tous les contrôles de sécurité dépendant de l'authentification deviennent inefficaces +- **Énumérer des utilisateurs ou des données** : extraire toutes les données de l'API sans avoir besoin de compte + +Le score CVSS 9.3 reflète le fait qu'il s'agit d'une faille exploitable réseau, sans authentification préalable requise, donnant un accès complet en lecture/écriture. + +## Comment corriger ? + +1. **Imposer l'authentification sur chaque endpoint protégé** : chaque route servant ou modifiant des données utilisateur doit valider les identifiants avant de traiter la requête. + +2. **Renvoyer 401 pour les requêtes non authentifiées** : renvoyez toujours `401 Unauthorized` (et non `200`, `404`, ou `500`) lorsqu'aucun identifiant valide n'est fourni. Un `403 Forbidden` est approprié lorsque l'utilisateur est authentifié mais ne dispose pas des permissions requises. + +3. **Appliquer un middleware d'authentification global** : utilisez un middleware au niveau de la passerelle ou du framework qui intercepte toutes les requêtes et impose l'authentification, plutôt que d'ajouter des contrôles d'auth endpoint par endpoint. + +4. **Tester l'accès non authentifié pour chaque nouveau endpoint** : ajoutez un test automatisé vérifiant que chaque endpoint protégé renvoie 401 lorsqu'aucun identifiant n'est fourni. + +```python +# Exemple FastAPI — imposer l'authentification globalement +from fastapi import Depends, HTTPException, Security +from fastapi.security import HTTPBearer + +security = HTTPBearer() + +@app.get("/api/users/profile") +async def get_profile(credentials = Security(security)): + # credentials.credentials est le Bearer token + user = verify_token(credentials.credentials) + return user +``` diff --git a/docs/fr/vulnerabilities/broken-authentication/brute-force-attack.mdx b/docs/fr/vulnerabilities/broken-authentication/brute-force-attack.mdx new file mode 100644 index 00000000..259e4bf8 --- /dev/null +++ b/docs/fr/vulnerabilities/broken-authentication/brute-force-attack.mdx @@ -0,0 +1,58 @@ +--- +title: Attaque par force brute sur l'authentification API +description: Une attaque par force brute, dans le contexte de l'authentification API, est une méthode d'essai-erreur utilisée par les attaquants pour deviner les bons identifiants (nom d'utilisateur et mot de passe) ou tokens d'accès. Les attaquants utilisent des outils automatisés pour effectuer de multiples requêtes vers le endpoint d'authentification, en essayant différentes combinaisons d'identifiants jusqu'à trouver la bonne. Cette attaque peut mener à un accès non autorisé à l'API et aux données sensibles. +--- + + + + + + + + + + + + + + + + + + +
GravitéÉlevée
CVEs + +
Classifications + +
Catégorie OWASP + OWASP API2:2023 Broken Authentication +
+ +Une attaque par force brute, dans le contexte de l'authentification API, est une méthode d'essai-erreur utilisée par les attaquants pour deviner les bons identifiants (nom d'utilisateur et mot de passe) ou tokens d'accès. Les attaquants utilisent des outils automatisés pour effectuer de multiples requêtes vers le endpoint d'authentification, en essayant différentes combinaisons d'identifiants jusqu'à trouver la bonne. Cette attaque peut mener à un accès non autorisé à l'API et aux données sensibles. + +## Exemple + +TODO : ajouter un exemple d'attaque par force brute sur un endpoint d'authentification API. + +## Comment tester ? + +TODO : ajouter les étapes pour tester la vulnérabilité d'attaque par force brute sur l'authentification API. + +## Quel est l'impact ? + +Lorsqu'un endpoint d'authentification API est vulnérable aux attaques par force brute, les attaquants peuvent obtenir un accès non autorisé à l'API et aux données sensibles. Cela peut mener à des fuites de données, des manipulations de données, et d'autres incidents de sécurité. + +## Comment corriger ? + +Pour prévenir les attaques par force brute sur les endpoints d'authentification API, envisagez de mettre en place les mesures de sécurité suivantes. S'il s'agit d'une authentification utilisateur, vous pouvez : +* Mettre en place des mécanismes de verrouillage de compte après un certain nombre de tentatives de connexion échouées pour l'authentification utilisateur. +* Mettre en place des défis CAPTCHA ou reCAPTCHA pour empêcher les attaques par force brute automatisées. + +Dans tous les cas, vous pouvez : +* Mettre en place une limitation de débit sur les requêtes d'authentification pour empêcher de multiples tentatives de connexion échouées. +* Surveiller les journaux d'authentification à la recherche d'activités suspectes et d'anomalies. diff --git a/docs/fr/vulnerabilities/broken-authentication/jwt-alg-none.mdx b/docs/fr/vulnerabilities/broken-authentication/jwt-alg-none.mdx new file mode 100644 index 00000000..26285ce4 --- /dev/null +++ b/docs/fr/vulnerabilities/broken-authentication/jwt-alg-none.mdx @@ -0,0 +1,124 @@ +--- +title: Algorithme JWT None +description: L'acceptation de l'algorithme « none » dans un JSON Web Token (JWT) survient lorsqu'un JWT est signé avec l'algorithme « none » ; cela signifie qu'il n'y a pas de signature, ce qui facilite la falsification du contenu du token par un attaquant sans être détecté. +--- + +import { Tabs, TabItem } from '@/components/docs' + + + + + + + + + + + + + + + + + + +
GravitéÉlevée
CVEs + +
Classifications + +
Catégorie OWASP + OWASP API2:2023 Broken Authentication +
+ +L'acceptation de l'algorithme « none » dans un JSON Web Token (JWT) survient lorsqu'un JWT est signé avec l'algorithme « none » ; cela signifie qu'il n'y a pas de signature, ce qui facilite la falsification du contenu du token par un attaquant sans être détecté. Cela peut mener à un accès non autorisé et à une manipulation des données. + +Pour plus de détails, consultez la [documentation JWTop sur l'algorithme none](https://www.cerberauth.com/docs/jwtop/vulnerabilities/none-algorithm/). + +## Exemple + +Voici un JWT valide signé avec l'algorithme RS512 : + +```bash +eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.MnECRBSUQEi8GjiAyWHPhPhhpzCiMLldkq-N_VS-iwI08c4xEVUhT1Xrx9kNGuwusiQuLI3AOBTPwtbdaasQDCOpF0nxxQNKkufJYFds61ooFZfXCuRyXe1yGnXPRzTfgr5YVe9-T8_JDccx5JP70d9hoO4DU4GNYQMvrOQl4xu8DEyyDT2hsjyTbrodVhrV9znMfEBCsYPPLI-Q-HYLquGThPdJe2kBNA-CiLRV6Mwzji67cTd_4P_oUHKXsAxMqVpo-xC2xiVpO2P9X1__uXrRrfiNFUur4B71UMgGYJ2z_cQqwFfSXz9glBIf_-BJU10Rkmyo2ew862d7WsHx8g +``` + +Ce JWT décodé contient ces parties : + +```json +{ + "alg": "RS512", + "typ": "JWT" +} +``` + +```json +{ + "sub": "1234567890", + "iat": 1516239022 +} +``` + +Le JWT suivant est invalide mais est autorisé à tort lorsque l'implémentation est vulnérable à cette attaque. + +```bash +eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ. +``` + +Le JWT invalide a cet en-tête avec l'algorithme défini sur none. + +```json +{ + "alg": "none", + "typ": "JWT" +} +``` + +## Comment tester ? + +Si vous voulez tester uniquement la vulnérabilité « JWT Alg None », vous pouvez utiliser la commande suivante : + + + +```bash +vulnapi scan curl [url] -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans jwt.alg_none +``` + + +```bash +echo "eyJhbGciOiJSUzUxMiI..." | vulnapi scan openapi [OpenAPI_Path_Or_URL] --scans jwt.alg_none +``` + + +```bash +vulnapi scan graphql -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans jwt.alg_none [url] +``` + + + +VulnAPI prend aussi en charge le scan d'autres types de [vulnérabilités](../). + +## Quel est l'impact ? + +Les impacts potentiels de la vulnérabilité JWT « alg none » sur la sécurité sont significatifs et peuvent inclure : + +- **Accès non autorisé** : les attaquants peuvent falsifier le payload du JWT pour modifier les rôles, permissions ou identifiants des utilisateurs, leur accordant ainsi un accès non autorisé à des ressources ou fonctionnalités du système. +- **Falsification des données** : sans signature pour garantir l'intégrité, les attaquants peuvent modifier les données du payload du JWT, altérant potentiellement des informations sensibles telles que les détails utilisateur, les paramètres de session, ou l'état de l'application. +- **Usurpation d'identité** : les attaquants peuvent forger des JWT en fabriquant leurs propres payloads et en les présentant comme des tokens valides, menant à des attaques d'usurpation d'identité où ils prennent l'identité d'utilisateurs ou de systèmes légitimes. +- **Élévation de privilèges** : en manipulant le payload du JWT, les attaquants peuvent élever leurs privilèges au sein du système en s'accordant des rôles ou permissions plus élevés que ceux initialement attribués. +- **Attaques par rejeu** : les attaquants peuvent capturer et rejouer des JWT valides puisqu'il n'y a pas de vérification de signature, leur permettant de réutiliser les tokens pour obtenir un accès non autorisé ou mener des actions malveillantes. +- **Déni de service (DDoS)** : dans certains cas, les attaquants peuvent exploiter la vulnérabilité pour fabriquer des JWT avec des payloads provoquant un comportement inattendu ou des erreurs dans l'application, pouvant mener à des interruptions de service ou des plantages du système. +- **Contournement des contrôles de sécurité** : dans les systèmes où les JWT sont utilisés pour le contrôle d'accès (autorisation) ou l'authentification, la vulnérabilité « alg none » peut contourner entièrement les contrôles de sécurité, rendant inefficace tout mécanisme de sécurité reposant sur les JWT. + +## Comment corriger ? + +Pour corriger la vulnérabilité JWT « alg none », vous devez vous assurer que la bibliothèque ou l'implémentation JWT utilisée n'est pas vulnérable à ce problème et est correctement configurée. + +- **Mise à jour de la bibliothèque** : assurez-vous que votre bibliothèque JWT est mise à jour vers une version corrigeant la vulnérabilité « alg none ». Vérifiez régulièrement les mises à jour de sécurité et correctifs fournis par les mainteneurs de la bibliothèque. +- **Configuration de la vérification d'algorithme** : configurez la bibliothèque ou l'implémentation JWT pour imposer la vérification de l'algorithme utilisé dans l'en-tête du JWT. Configurez-la spécifiquement pour rejeter les tokens signés avec l'algorithme « none ». Cela garantit que seuls les tokens signés avec des algorithmes sécurisés (ex. HMAC, RSA) sont acceptés. diff --git a/docs/fr/vulnerabilities/broken-authentication/jwt-algorithm-confusion.mdx b/docs/fr/vulnerabilities/broken-authentication/jwt-algorithm-confusion.mdx new file mode 100644 index 00000000..f59670ff --- /dev/null +++ b/docs/fr/vulnerabilities/broken-authentication/jwt-algorithm-confusion.mdx @@ -0,0 +1,90 @@ +--- +title: Confusion d'algorithme JWT +description: La confusion d'algorithme JWT, aussi connue sous le nom d'attaque de confusion HMAC, survient lorsqu'un attaquant trompe un serveur pour qu'il vérifie un JWT avec un algorithme symétrique (comme HS256) au lieu d'un algorithme asymétrique (comme RS256). +--- + +import { Tabs, TabItem } from '@/components/docs' + + + + + + + + + + + + + + + + + + +
GravitéÉlevée
CVEs + +
Classifications + +
Catégorie OWASP + OWASP API2:2023 Broken Authentication +
+ +La confusion d'algorithme JWT, aussi connue sous le nom d'attaque de confusion HMAC, survient lorsqu'un attaquant trompe un serveur pour qu'il vérifie un JWT avec un algorithme symétrique (comme HS256) au lieu d'un algorithme asymétrique (comme RS256). Cela se produit généralement lorsqu'un serveur utilise une clé publique (prévue pour RS256) comme secret pour la vérification HS256. + +Pour plus de détails, consultez la [documentation jwtop sur la confusion HMAC](https://www.cerberauth.com/docs/jwtop/vulnerabilities/hmac-confusion/). + +## Exemple + +Une application s'attend à recevoir un JWT signé avec un algorithme asymétrique comme RS256. Le serveur utilise une clé publique pour vérifier la signature. + +Si l'application est vulnérable, un attaquant peut : +1. Obtenir la clé publique (souvent publique par nature). +2. Créer un nouveau JWT avec l'en-tête d'algorithme défini sur `HS256`. +3. Signer le JWT en utilisant la clé publique comme secret HMAC. +4. Envoyer le token forgé au serveur. + +Le serveur, voyant `HS256` dans l'en-tête, peut utiliser sa clé publique comme secret pour vérifier la signature HMAC, et s'il n'impose pas strictement l'algorithme, la vérification réussira. + +## Comment tester ? + +Si vous voulez tester uniquement la vulnérabilité « Confusion d'algorithme JWT », vous pouvez utiliser la commande suivante : + + + +```bash +vulnapi scan curl [url] -H "Authorization: Bearer [JWT]" --scans jwt.hmac_confusion +``` + + +```bash +echo "[JWT]" | vulnapi scan openapi [OpenAPI_Path_Or_URL] --scans jwt.hmac_confusion +``` + + +```bash +vulnapi scan graphql -H "Authorization: Bearer [JWT]" --scans jwt.hmac_confusion [url] +``` + + + +VulnAPI prend aussi en charge le scan d'autres types de [vulnérabilités](../). + +## Quel est l'impact ? + +Les impacts potentiels de la confusion d'algorithme JWT sur la sécurité sont : + +- **Contournement complet de l'authentification** : un attaquant peut forger des tokens pour n'importe quel utilisateur, y compris des comptes administrateurs. +- **Accès non autorisé** : obtention d'un accès à des données et fonctionnalités sensibles. +- **Prise de contrôle de compte** : usurpation de l'identité de n'importe quel utilisateur du système. + +## Comment corriger ? + +- **Imposition stricte de l'algorithme** : spécifiez toujours le ou les algorithmes attendus lors de la vérification d'un JWT. Ne vous fiez pas à l'en-tête `alg` du token. +- **Utiliser des bibliothèques sécurisées** : utilisez des bibliothèques JWT non vulnérables à cette attaque et assurez-vous qu'elles sont correctement configurées. +- **Séparer les clés** : utilisez des clés différentes pour des usages différents et assurez-vous que les clés publiques ne sont utilisées que pour la vérification asymétrique. diff --git a/docs/fr/vulnerabilities/broken-authentication/jwt-blank-secret.mdx b/docs/fr/vulnerabilities/broken-authentication/jwt-blank-secret.mdx new file mode 100644 index 00000000..de1311f8 --- /dev/null +++ b/docs/fr/vulnerabilities/broken-authentication/jwt-blank-secret.mdx @@ -0,0 +1,104 @@ +--- +title: Secret JWT vide +description: Une vulnérabilité survient lorsqu'un JSON Web Token (JWT) est signé avec un secret vide. Dans ce scénario, le token manque d'une protection cryptographique adéquate, le rendant susceptible d'être manipulé. +--- + +import { Tabs, TabItem } from '@/components/docs' + + + + + + + + + + + + + + + + + + +
GravitéÉlevée
CVEs + +
Classifications + CWE-287: Improper Authentication +
Catégorie OWASP + OWASP API2:2023 Broken Authentication +
+ +Une vulnérabilité survient lorsqu'un JSON Web Token (JWT) est signé avec un secret vide. Dans ce scénario, le token manque d'une protection cryptographique adéquate, le rendant susceptible d'être manipulé. Les attaquants peuvent modifier les claims et le contenu du token sans être détectés, pouvant mener à un accès non autorisé et à une falsification des données. + +Pour plus de détails, consultez la [documentation jwtop sur le secret vide](https://www.cerberauth.com/docs/jwtop/vulnerabilities/blank-secret/). + +## Exemple + +Voici un JWT valide signé avec l'algorithme HS256 : + +```bash +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MTYyNDI2MjIsImlhdCI6MTUxNjIzOTAyMiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.SCC35SSgMSMr0kV1i_TuPAhiSGtsC1cFGCfvaus5GyU +``` + +Ce JWT décodé contient ces parties : + +```json +{ + "alg": "HS256", + "typ": "JWT" +} +``` + +```json +{ + "iat": 1516239022, + "exp": 1516242622, + "name": "John Doe", + "sub": "2cb307ba-bb46-4194-854f-4774046d9c9b" +} +``` + +Le JWT suivant est signé avec un secret vide (clé vide — même en-tête et payload, signature différente) : + +```bash +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MTYyNDI2MjIsImlhdCI6MTUxNjIzOTAyMiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.rpOjSoJBjzZifWAc-MBQ4UVS9tOY7gJV8cN0j6bN1oA +``` + +## Comment tester ? + +Si vous voulez tester uniquement la vulnérabilité « Secret JWT vide », vous pouvez utiliser la commande suivante : + + + +```bash +vulnapi scan curl [url] -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans jwt.blank_secret +``` + + +```bash +echo "eyJhbGciOiJSUzUxMiI..." | vulnapi scan openapi [OpenAPI_Path_Or_URL] --scans jwt.blank_secret +``` + + +```bash +vulnapi scan graphql -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans jwt.blank_secret [url] +``` + + + +VulnAPI prend aussi en charge le scan d'autres types de [vulnérabilités](../). + +## Quel est l'impact ? + +Signer un JWT avec un secret vide a un impact significatif sur la sécurité du token. Un secret vide signifie qu'aucune clé secrète n'est utilisée pour signer le token, ce qui le rend vulnérable à la falsification et à l'accès non autorisé. + +En signant un JWT avec un secret vide, toute personne ayant accès au token peut en modifier le contenu sans être détectée. Cela peut mener à divers risques de sécurité, tels que l'usurpation d'identité, la falsification de données, et l'accès non autorisé à des ressources protégées. + +## Comment corriger ? + +Pour corriger la vulnérabilité de secret JWT vide, assurez-vous que tous les JWT sont signés avec une clé secrète sécurisée. Utilisez des algorithmes cryptographiques robustes et gardez la clé secrète confidentielle pour empêcher l'accès non autorisé et la falsification des tokens. diff --git a/docs/fr/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.excalidraw b/docs/fr/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.excalidraw new file mode 100644 index 00000000..e74e102a --- /dev/null +++ b/docs/fr/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.excalidraw @@ -0,0 +1,1235 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "text", + "version": 2035, + "versionNonce": 1818462084, + "index": "b6H", + "isDeleted": false, + "id": "ltyIvJkLl-pTJBLTGiDvt", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -505.87472326456395, + "y": 2309.5011349340184, + "strokeColor": "#000000", + "backgroundColor": "white", + "width": 103.51054382324219, + "height": 28.8310711010254, + "seed": 1804201402, + "groupIds": [ + "TafrumkQql6FUeTFdBMm-" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1730152817116, + "link": null, + "locked": false, + "fontSize": 23.064856880820322, + "fontFamily": 1, + "text": "End User", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "End User", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 2123, + "versionNonce": 1628436228, + "index": "b6I", + "isDeleted": false, + "id": "pijBT8vPR7p7LIf6w_dcC", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": -476.24781473924645, + "y": 2292.555987811961, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 56.819606873238214, + "height": 50.596228152466416, + "seed": 104343162, + "groupIds": [ + "XJCcWbsBNKnnIezwbpXsC", + "TafrumkQql6FUeTFdBMm-" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1730152817116, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 6.363795969802675, + -33.527621064887384 + ], + [ + 27.273411299154343, + -50.596228152466416 + ], + [ + 48.183026628506006, + -37.185179726511414 + ], + [ + 56.819606873238214, + -3.481418584602479 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 1860, + "versionNonce": 1971484292, + "index": "b6J", + "isDeleted": false, + "id": "pcQc9n9kEAl-OVAUytn-b", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": -461.88547731665426, + "y": 2215.6450141913247, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 29.091638719097993, + "height": 25.455183879210832, + "seed": 1210140474, + "groupIds": [ + "XJCcWbsBNKnnIezwbpXsC", + "TafrumkQql6FUeTFdBMm-" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1730152817116, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 2228, + "versionNonce": 171340604, + "index": "b8p", + "isDeleted": false, + "id": "IDPR-T_GInZ_SYrUKdyiV", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 232.9885694829697, + "y": 1901.165608184967, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 215.2445549281264, + "height": 191.3848126773838, + "seed": 553856166, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "NIxHFrLYNc4GZ_eaCUNEX", + "type": "text" + }, + { + "id": "QM3OeE-gZsJtTqpVyK6WY", + "type": "arrow" + }, + { + "id": "L746tARKZ5EBHf_R0vgrz", + "type": "arrow" + }, + { + "id": "KNsMX2SBrRRRZSGEwUC8b", + "type": "arrow" + }, + { + "id": "1DW6jYie7TBBMPB2Tb6PL", + "type": "arrow" + } + ], + "updated": 1730152887733, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2546, + "versionNonce": 234662844, + "index": "b8q", + "isDeleted": false, + "id": "NIxHFrLYNc4GZ_eaCUNEX", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 283.41664833863445, + "y": 1961.8580145236588, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 114.38839721679688, + "height": 70, + "seed": 2122491878, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1730152887733, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 5, + "text": "Token\nProvider", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "IDPR-T_GInZ_SYrUKdyiV", + "originalText": "Token Provider", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2186, + "versionNonce": 741797636, + "index": "b91", + "isDeleted": false, + "id": "0pKo0FuXTqVcAnFHg3eHW", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -205.52933980495288, + "y": 2178.953941971566, + "strokeColor": "#e03131", + "backgroundColor": "transparent", + "width": 215.2445549281264, + "height": 191.3848126773838, + "seed": 236441020, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "L9qPfblCPCj76bIqH4zPF", + "type": "text" + }, + { + "id": "5zc8kBUOyFDyV3r5cJ5u1", + "type": "arrow" + }, + { + "id": "GMVGnrJnqaFoheEbd232T", + "type": "arrow" + }, + { + "id": "1DW6jYie7TBBMPB2Tb6PL", + "type": "arrow" + } + ], + "updated": 1730152887733, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2442, + "versionNonce": 493343804, + "index": "b92", + "isDeleted": false, + "id": "L9qPfblCPCj76bIqH4zPF", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -187.50743465534282, + "y": 2257.146348310258, + "strokeColor": "#e03131", + "backgroundColor": "transparent", + "width": 179.20074462890625, + "height": 35, + "seed": 1909372476, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1730152887733, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 5, + "text": "Malicious App", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "0pKo0FuXTqVcAnFHg3eHW", + "originalText": "Malicious App", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 2141, + "versionNonce": 1555780868, + "index": "b93", + "isDeleted": false, + "id": "T3PyqevJ-r5aTrOqjBFmC", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 682.0542956691074, + "y": 1874.6210808258843, + "strokeColor": "#000000", + "backgroundColor": "white", + "width": 120.95199584960938, + "height": 28.8310711010254, + "seed": 824973372, + "groupIds": [ + "B6zTjTN5MAfYoqaWmyIpC" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1730152736532, + "link": null, + "locked": false, + "fontSize": 23.064856880820322, + "fontFamily": 1, + "text": "Developers", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Developers", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 2219, + "versionNonce": 1611797636, + "index": "b94", + "isDeleted": false, + "id": "l7ybVNV2OMm-nKOKuikqR", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 720.4019302076085, + "y": 1857.6759337038266, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 56.819606873238214, + "height": 50.596228152466416, + "seed": 1895149756, + "groupIds": [ + "aNKxm8YeKZVFgjEt1CgRW", + "B6zTjTN5MAfYoqaWmyIpC" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1730152736532, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 6.363795969802675, + -33.527621064887384 + ], + [ + 27.273411299154343, + -50.596228152466416 + ], + [ + 48.183026628506006, + -37.185179726511414 + ], + [ + 56.819606873238214, + -3.481418584602479 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 1956, + "versionNonce": 1499784196, + "index": "b95", + "isDeleted": false, + "id": "K8bdPFGmyuaYudSU_OE_3", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 734.7642676302007, + "y": 1780.7649600831905, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 29.091638719097993, + "height": 25.455183879210832, + "seed": 620247356, + "groupIds": [ + "aNKxm8YeKZVFgjEt1CgRW", + "B6zTjTN5MAfYoqaWmyIpC" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1730152736532, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2229, + "versionNonce": 1766236476, + "index": "b96", + "isDeleted": false, + "id": "drKxgai7tWhbRkPAkFoaW", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 706.11979529216, + "y": 2290.73338734735, + "strokeColor": "#e03131", + "backgroundColor": "white", + "width": 102.93438720703125, + "height": 28.8310711010254, + "seed": 373772420, + "groupIds": [ + "6MwyX96Lf9eym84D59YgW" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1730152840065, + "link": null, + "locked": false, + "fontSize": 23.064856880820322, + "fontFamily": 1, + "text": "Attacker", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Attacker", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 2273, + "versionNonce": 920066492, + "index": "b97", + "isDeleted": false, + "id": "ANLjDXSBmq2lDuXiHhPKE", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 735.458625509372, + "y": 2273.7882402252926, + "strokeColor": "#e03131", + "backgroundColor": "#ced4da", + "width": 56.819606873238214, + "height": 50.596228152466416, + "seed": 227713028, + "groupIds": [ + "kFpzVapCBJpFzZjwFXhER", + "6MwyX96Lf9eym84D59YgW" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1730152840065, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 6.363795969802675, + -33.527621064887384 + ], + [ + 27.273411299154343, + -50.596228152466416 + ], + [ + 48.183026628506006, + -37.185179726511414 + ], + [ + 56.819606873238214, + -3.481418584602479 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "ellipse", + "version": 2010, + "versionNonce": 1556382268, + "index": "b98", + "isDeleted": false, + "id": "wS4NSv0k6ivd-min90xVs", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 749.8209629319642, + "y": 2196.877266604656, + "strokeColor": "#e03131", + "backgroundColor": "#ced4da", + "width": 29.091638719097993, + "height": 25.455183879210832, + "seed": 768081796, + "groupIds": [ + "kFpzVapCBJpFzZjwFXhER", + "6MwyX96Lf9eym84D59YgW" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1730152840065, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 3307, + "versionNonce": 752590212, + "index": "b99", + "isDeleted": false, + "id": "QM3OeE-gZsJtTqpVyK6WY", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 701.9411573717334, + "y": 1844.8912709167275, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 244.26981175285607, + "height": 120.72995808263295, + "seed": 306836540, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "R_kzZWp-mTJGBEknO4BDX" + } + ], + "updated": 1730152887734, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "IDPR-T_GInZ_SYrUKdyiV", + "focus": 0.17879744648147, + "gap": 9.438221207781197, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -244.26981175285607, + 120.72995808263295 + ] + ] + }, + { + "id": "R_kzZWp-mTJGBEknO4BDX", + "type": "text", + "x": 121.65945464057293, + "y": 1853.7601556245634, + "width": 144.31985473632812, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b9A", + "roundness": null, + "seed": 235483396, + "version": 19, + "versionNonce": 1684440708, + "isDeleted": false, + "boundElements": null, + "updated": 1730152612311, + "link": null, + "locked": false, + "text": "Create Project", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "QM3OeE-gZsJtTqpVyK6WY", + "originalText": "Create Project", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 3418, + "versionNonce": 147191356, + "index": "b9B", + "isDeleted": false, + "id": "L746tARKZ5EBHf_R0vgrz", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 716.5736657298296, + "y": 2249.302134297232, + "strokeColor": "#e03131", + "backgroundColor": "transparent", + "width": 256.56483322792286, + "height": 232.4291067895324, + "seed": 1422419204, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "siYG6VKVSb_LJCZ4mZhGR" + } + ], + "updated": 1730152923070, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "IDPR-T_GInZ_SYrUKdyiV", + "focus": -0.4562902862666964, + "gap": 11.775708090810582, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -256.56483322792286, + -232.4291067895324 + ] + ] + }, + { + "type": "text", + "version": 61, + "versionNonce": 69135492, + "index": "b9C", + "isDeleted": false, + "id": "siYG6VKVSb_LJCZ4mZhGR", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 516.1313217477041, + "y": 2120.5875809024656, + "strokeColor": "#e03131", + "backgroundColor": "transparent", + "width": 144.31985473632812, + "height": 25, + "seed": 1945969796, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1730152921956, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 5, + "text": "Create Project", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "L746tARKZ5EBHf_R0vgrz", + "originalText": "Create Project", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 3257, + "versionNonce": 493737532, + "index": "b9D", + "isDeleted": false, + "id": "5zc8kBUOyFDyV3r5cJ5u1", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -389.669252006124, + "y": 2282.7876018612146, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 169.1290066673917, + "height": 5.596252304153495, + "seed": 1553075644, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "7RKMXj2cxBpP35OoP6w88" + } + ], + "updated": 1730152887734, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "0pKo0FuXTqVcAnFHg3eHW", + "focus": 0.015241445198305226, + "gap": 15.010905533779408, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 169.1290066673917, + -5.596252304153495 + ] + ] + }, + { + "id": "7RKMXj2cxBpP35OoP6w88", + "type": "text", + "x": -349.63218044871417, + "y": 2182.1009987335074, + "width": 49.35993957519531, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b9E", + "roundness": null, + "seed": 1020353412, + "version": 7, + "versionNonce": 286799876, + "isDeleted": false, + "boundElements": null, + "updated": 1730152666495, + "link": null, + "locked": false, + "text": "Login", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "5zc8kBUOyFDyV3r5cJ5u1", + "originalText": "Login", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2219, + "versionNonce": 1519381636, + "index": "b9F", + "isDeleted": false, + "id": "BVL6kArEACCUWcQSWHFae", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -197.31659691308204, + "y": 1891.50794075608, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 215.2445549281264, + "height": 191.3848126773838, + "seed": 1674875964, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "L40neTwxvfRhgN0RNPCjk", + "type": "text" + }, + { + "id": "KNsMX2SBrRRRZSGEwUC8b", + "type": "arrow" + }, + { + "id": "GMVGnrJnqaFoheEbd232T", + "type": "arrow" + } + ], + "updated": 1730152771829, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2484, + "versionNonce": 1616512188, + "index": "b9G", + "isDeleted": false, + "id": "L40neTwxvfRhgN0RNPCjk", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -158.99460478837432, + "y": 1969.7003470947718, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 138.60057067871094, + "height": 35, + "seed": 825142460, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1730152713281, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 5, + "text": "Victim App", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "BVL6kArEACCUWcQSWHFae", + "originalText": "Victim App", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 3243, + "versionNonce": 1053846276, + "index": "b9H", + "isDeleted": false, + "id": "KNsMX2SBrRRRZSGEwUC8b", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 20.480215068902226, + "y": 1988.10732399155, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 203.34876871685455, + "height": 2.0759069603368516, + "seed": 1956413316, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "qohNI9nUisxSE0t98F5c6" + } + ], + "updated": 1730152960724, + "link": null, + "locked": false, + "startBinding": { + "elementId": "BVL6kArEACCUWcQSWHFae", + "focus": -0.020951095762841803, + "gap": 2.5522570538578577, + "fixedPoint": null + }, + "endBinding": { + "elementId": "IDPR-T_GInZ_SYrUKdyiV", + "focus": 0.12417236481001943, + "gap": 9.159585697212947, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 203.34876871685455, + -2.0759069603368516 + ] + ] + }, + { + "type": "text", + "version": 53, + "versionNonce": 1016397884, + "index": "b9I", + "isDeleted": false, + "id": "qohNI9nUisxSE0t98F5c6", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 52.16467022811075, + "y": 1937.0693705113815, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 139.9798583984375, + "height": 100, + "seed": 1734995716, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1730152933693, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 5, + "text": "Login &\nVerify\nToken\n(not audience)", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "KNsMX2SBrRRRZSGEwUC8b", + "originalText": "Login &\nVerify\nToken\n(not audience)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 3326, + "versionNonce": 709865148, + "index": "b9J", + "isDeleted": false, + "id": "GMVGnrJnqaFoheEbd232T", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -85.78012969943508, + "y": 2175.762596605995, + "strokeColor": "#e03131", + "backgroundColor": "transparent", + "width": 7.444965507835249, + "height": 93.7848692528969, + "seed": 244411196, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "BK1Fa_XotI7vFgkPy2dbx" + } + ], + "updated": 1730152887734, + "link": null, + "locked": false, + "startBinding": { + "elementId": "0pKo0FuXTqVcAnFHg3eHW", + "focus": 0.1733804513331944, + "gap": 3.1913453655711237, + "fixedPoint": null + }, + "endBinding": { + "elementId": "BVL6kArEACCUWcQSWHFae", + "focus": 0.09594391447305084, + "gap": 1, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -7.444965507835249, + -93.7848692528969 + ] + ] + }, + { + "type": "text", + "version": 52, + "versionNonce": 107945148, + "index": "b9K", + "isDeleted": false, + "id": "BK1Fa_XotI7vFgkPy2dbx", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -178.43252883518863, + "y": 2116.3701619795465, + "strokeColor": "#e03131", + "backgroundColor": "transparent", + "width": 177.85983276367188, + "height": 25, + "seed": 1010344892, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1730152787158, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 5, + "text": "Use stolen tokens", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "GMVGnrJnqaFoheEbd232T", + "originalText": "Use stolen tokens", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 3432, + "versionNonce": 1305529476, + "index": "b9L", + "isDeleted": false, + "id": "1DW6jYie7TBBMPB2Tb6PL", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 26.268982827136426, + "y": 2288.1972340648817, + "strokeColor": "#e03131", + "backgroundColor": "transparent", + "width": 288.2137785995216, + "height": 181.38746009952138, + "seed": 647081148, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "7jSYHzSnihK5XvjTuh4_z" + } + ], + "updated": 1730152951210, + "link": null, + "locked": false, + "startBinding": { + "elementId": "0pKo0FuXTqVcAnFHg3eHW", + "focus": 0.5611221909850164, + "gap": 16.553767703962905, + "fixedPoint": null + }, + "endBinding": { + "elementId": "IDPR-T_GInZ_SYrUKdyiV", + "focus": -0.5721787148148761, + "gap": 14.259353103009744, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 288.2137785995216, + -181.38746009952138 + ] + ] + }, + { + "id": "7jSYHzSnihK5XvjTuh4_z", + "type": "text", + "x": 88.62596367963161, + "y": 2172.503504015121, + "width": 163.49981689453125, + "height": 50, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b9LV", + "roundness": null, + "seed": 1300461060, + "version": 64, + "versionNonce": 1109535932, + "isDeleted": false, + "boundElements": null, + "updated": 1730152950133, + "link": null, + "locked": false, + "text": "Login with\nAttacker project", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "1DW6jYie7TBBMPB2Tb6PL", + "originalText": "Login with\nAttacker project", + "autoResize": true, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/fr/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.mdx b/docs/fr/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.mdx new file mode 100644 index 00000000..51f5ec3a --- /dev/null +++ b/docs/fr/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.mdx @@ -0,0 +1,115 @@ +--- +title: Attaque de relais inter-services de token +description: Une vulnérabilité survient lorsqu'un token est forgé par le même service mais que l'émetteur n'est pas vérifié. Cela peut mener à des risques de sécurité, car cela signifie qu'un attaquant pourrait créer un token forgé et manipuler les champs ou usurper l'identité d'un utilisateur. +--- + + + + + + + + + + + + + + +
GravitéÉlevée
Classifications + +
Catégorie OWASP + OWASP API2:2023 Broken Authentication +
+ +Une vulnérabilité survient lorsqu'un JSON Web Token (JWT) est signé par le même service mais que l'émetteur (la source du token) et/ou l'audience (le destinataire prévu) ne sont pas vérifiés. Cela peut mener à des risques de sécurité, car cela signifie qu'un attaquant pourrait créer un JWT forgé avec la signature du même service et manipuler les champs d'émetteur et d'audience. Sans vérification adéquate, le service peut accepter le token forgé, accordant potentiellement un accès non autorisé ou compromettant la sécurité du système. + +Pour plus de détails, consultez la [documentation jwtop sur l'attaque de relais inter-services](https://www.cerberauth.com/docs/jwtop/vulnerabilities/cross-service-relay-attack/). + +![Attaque de relais inter-services JWT](./jwt-cross-service-relay-attack.png) + +## Services concernés + +### Fournisseurs d'identité sociale + +La plupart des fournisseurs d'identité sociale génèrent des ID tokens en utilisant la même paire de clés. En plus des vérifications d'intégrité du JWT, l'`issuer` et l'`audience` doivent être vérifiés pour s'assurer que le token a été généré depuis le client attendu. + +Voici quelques fournisseurs d'identité sociale concernés par cette vulnérabilité : + +| Service | Description | Documentation | +| ------- | ----------- | ------------- | +| **Google** | Google fournit des ID tokens générés depuis la même paire de clés. L'`audience` doit être vérifiée pour s'assurer que le token a été généré depuis le `Client Id` attendu du projet Google. | [Valider un Id Token Google](https://developers.google.com/identity/openid-connect/openid-connect#validatinganidtoken) | +| **Facebook** | Facebook fournit des ID tokens générés depuis la même paire de clés. L'`audience` doit être vérifiée pour s'assurer que le token a été généré depuis le `Facebook App Id` attendu. | [Valider un token Facebook](https://developers.facebook.com/docs/facebook-login/limited-login/token/validating/) | +| **Microsoft** | Microsoft fournit des ID tokens générés depuis la même paire de clés. L'`audience` doit être vérifiée pour s'assurer que le token a été généré depuis le `Microsoft App Id` attendu. | [Valider les Id Tokens Microsoft](https://learn.microsoft.com/en-us/entra/identity-platform/id-tokens#validate-tokens) | +| **Apple** | Apple fournit des ID tokens générés depuis la même paire de clés. L'`audience` doit être vérifiée pour s'assurer que le token a été généré depuis le `client_id` attendu de l'app Apple. | [Valider les Id Tokens Apple](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple#3383773) | + +### Services IAM / d'autorisation + +Certains services IAM génèrent des tokens JWT en utilisant la même paire de clés. En plus des vérifications d'intégrité du JWT, des contrôles supplémentaires doivent être effectués pour s'assurer que le token a été généré depuis le client / tenant attendu. + +Voici quelques services concernés : + +| Service | Description | Documentation | +| ------------------------------------------- | ----------- | ------------- | +| **Firebase** / **Google Identity Platform** | Firebase fournit des ID tokens générés depuis la même paire de clés. L'émetteur et l'audience doivent être vérifiés pour s'assurer que le token a été généré depuis le projet Firebase attendu. | [Vérifier un Id Token Firebase](https://firebase.google.com/docs/auth/admin/verify-id-tokens) | + +## Exemple + +Voici un exemple de JWT (ID Token) émis par Google : + +```json +{ + "iss": "https://accounts.google.com", + "azp": "1234987819200.apps.googleusercontent.com", + "aud": "1234987819200.apps.googleusercontent.com", + "sub": "10769150350006150715113082367", + "at_hash": "HK6E_P6Dh8Y93mRNtsDB1Q", + "hd": "example.com", + "email": "jsmith@example.com", + "email_verified": "true", + "iat": 1353601026, + "exp": 1353604926, + "nonce": "0394852-3190485-2490358" +} +``` + +Dans cet exemple, le claim `aud` représente l'audience, qui est l'ID client du projet Google. Le service doit vérifier le claim `aud` pour s'assurer que le token a été généré depuis le `Client Id` attendu du projet Google. + +Supposons qu'un attaquant crée un nouveau projet avec le même service et génère un token. L'attaquant peut relayer le token vers le projet de la victime et usurper l'identité de l'utilisateur, faisant croire au projet de la victime que l'utilisateur est authentifié. + +Voici un exemple de token forgé : + +```json +{ + "iss": "https://accounts.google.com", + "azp": "1234987819201.apps.googleusercontent.com", + "aud": "1234987819201.apps.googleusercontent.com", + "sub": "10769150350006150715113082367", + "at_hash": "HK6E_P6Dh8Y93mRNtsDB1Q", + "hd": "example.com", + "email": "jsmith@example.com", + "email_verified": "true", + "iat": 1353601026, + "exp": 1353604926, + "nonce": "0394852-3190485-2490358" +} +``` + +Remarquez que le claim `aud` a été changé pour le `Client Id` du projet de l'attaquant. Si le service ne vérifie pas le claim `aud`, il acceptera le token forgé et croira que l'utilisateur est authentifié. + +## Comment tester ? + +Créez un nouveau projet avec le même service et générez un token (via un processus d'authentification légitime). Puis, essayez de relayer le token vers votre projet d'origine et voyez si le service accepte le token ou non. Si le service accepte le token, alors il est vulnérable à cette attaque. + +Ce test nécessite des étapes manuelles. VulnAPI n'automatise pas encore ce scan. Créez un second projet avec le même fournisseur d'identité, obtenez un token valide pour ce projet, puis envoyez-le à l'API de votre projet d'origine. Si l'API accepte la requête, elle est vulnérable. + +## Quel est l'impact ? + +L'impact de cette vulnérabilité est qu'un attaquant pourrait créer un autre projet avec le même service. Lorsqu'un utilisateur s'authentifie avec le projet malveillant, l'attaquant peut relayer le token vers le projet de la victime et usurper l'identité de l'utilisateur, faisant croire au projet de la victime que l'utilisateur est authentifié. + +## Comment corriger ? + +Pour corriger cette vulnérabilité, le service doit suivre les recommandations du fournisseur de tokens. La plupart du temps, l'`issuer` et l'`audience` doivent être vérifiés pour s'assurer que le token a été généré depuis le client attendu. Le service doit continuer à vérifier l'intégrité du token et les autres claims pour s'assurer qu'il n'a pas été falsifié. diff --git a/docs/fr/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.png b/docs/fr/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.png new file mode 100644 index 0000000000000000000000000000000000000000..9469bab54c2178db164c1285949d74070e4e7456 GIT binary patch literal 100233 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

tf&!-8LVOcPgP4C& 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 DZ8i+o literal 0 HcmV?d00001 diff --git a/docs/fr/vulnerabilities/broken-authentication/jwt-expired.mdx b/docs/fr/vulnerabilities/broken-authentication/jwt-expired.mdx new file mode 100644 index 00000000..55612c70 --- /dev/null +++ b/docs/fr/vulnerabilities/broken-authentication/jwt-expired.mdx @@ -0,0 +1,66 @@ +--- +title: JWT expiré +description: Une vulnérabilité survient lorsqu'un JSON Web Token (JWT) est accepté par le serveur même après le passage de son heure d'expiration (claim `exp`). +--- + +import { Tabs, TabItem } from '@/components/docs' + + + + + + + + + + + + + + +
GravitéMoyenne
Classifications + +
Catégorie OWASP + OWASP API2:2023 Broken Authentication +
+ +Une vulnérabilité survient lorsqu'un JSON Web Token (JWT) est accepté par le serveur même après le passage de son heure d'expiration (claim `exp`). Les JWT sont conçus pour être de courte durée de vie, et une fois expirés, ils ne devraient plus être considérés comme valides pour l'authentification ou l'autorisation. + +## Exemple + +Un JWT contient un claim `exp`, qui est un timestamp Unix indiquant quand le token expire : + +```json +{ + "sub": "1234567890", + "name": "John Doe", + "iat": 1516239022, + "exp": 1516242622 +} +``` + +Si l'heure actuelle est `1516243000` (soit après l'heure `exp`), le serveur devrait rejeter ce token. S'il l'accepte, il est vulnérable à l'utilisation de tokens expirés. + +## Comment tester ? + +Pour tester si le serveur accepte des tokens expirés, vous pouvez obtenir un token valide, attendre son expiration, puis essayer de l'utiliser. Autrement, si vous disposez de la clé de signature, vous pouvez créer un token déjà expiré. + +VulnAPI n'automatise pas encore ce scan. Vous pouvez utiliser des outils comme [jwtop](https://github.com/cerberauth/jwtop) pour créer des tokens expirés à des fins de test manuel. + +## Quel est l'impact ? + +L'impact de l'acceptation de tokens expirés inclut : + +- **Fenêtre d'opportunité prolongée** : les attaquants peuvent utiliser des tokens volés pendant une période plus longue. +- **Contournement de la révocation** : si un utilisateur est déconnecté ou si son accès est révoqué, un token expiré peut tout de même accorder l'accès si le serveur ne vérifie pas l'expiration. +- **Détournement de session** : risque accru de détournement de session si les tokens restent valides trop longtemps ou indéfiniment. + +## Comment corriger ? + +- **Vérification stricte de l'expiration** : assurez-vous que votre bibliothèque ou implémentation JWT vérifie strictement le claim `exp`. +- **Tokens de courte durée** : utilisez des durées d'expiration courtes pour les tokens d'accès (ex. 15 à 60 minutes). +- **Utiliser des tokens de rafraîchissement** : mettez en place des tokens de rafraîchissement pour permettre aux utilisateurs d'obtenir de nouveaux tokens d'accès sans se réauthentifier, tout en gardant les tokens d'accès de courte durée. +- **Prise en compte du décalage d'horloge** : autorisez un petit décalage d'horloge raisonnable (ex. quelques minutes) lors de la vérification de l'expiration, sans pour autant l'ignorer complètement. diff --git a/docs/fr/vulnerabilities/broken-authentication/jwt-kid-injection.mdx b/docs/fr/vulnerabilities/broken-authentication/jwt-kid-injection.mdx new file mode 100644 index 00000000..a80188d5 --- /dev/null +++ b/docs/fr/vulnerabilities/broken-authentication/jwt-kid-injection.mdx @@ -0,0 +1,99 @@ +--- +title: Injection d'en-tête JWT KID +description: L'injection d'en-tête JWT KID survient lorsque le paramètre d'en-tête `kid` (Key ID) est transmis sans désinfection à une requête de base de données ou une recherche sur le système de fichiers, permettant aux attaquants de forger des tokens de confiance. +--- + +import { Tabs, TabItem } from '@/components/docs' + + + + + + + + + + + + + + +
GravitéÉlevée
Classifications + CWE-345: Insufficient Verification of Data Authenticity +
Catégorie OWASP + OWASP API2:2023 Broken Authentication +
+ +Le champ d'en-tête JWT `kid` (Key ID) est censé indiquer au serveur quelle clé utiliser pour la vérification de la signature. Lorsque cette valeur est transmise sans désinfection à une requête de base de données ou une recherche sur le système de fichiers, les attaquants peuvent injecter un payload amenant le serveur à utiliser une clé contrôlée par l'attaquant, leur permettant ainsi de forger des tokens arbitraires. + +Pour plus de détails, consultez la [documentation jwtop sur l'injection d'en-tête KID](https://www.cerberauth.com/docs/jwtop/vulnerabilities/kid-header-injection/). + +VulnAPI teste deux variantes de cette attaque : + +- **Injection SQL** — injecte un payload SQL dans `kid` (ex. `' UNION SELECT 'secret' ...`) pour que la base de données renvoie une valeur connue en tant que clé. +- **Traversée de chemin** — définit `kid` avec un chemin du système de fichiers tel que `/dev/null` pour que le serveur lise un fichier vide comme clé, ce qui peut correspondre à un secret HMAC vide. + +## Exemple + +Voici un JWT valide signé avec HS256 : + +``` +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c +``` + +### Variante par injection SQL + +L'attaquant fabrique un token avec un en-tête `kid` qui s'injecte dans la requête de recherche de clé et le re-signe avec la valeur que la requête est censée renvoyer (`secret`) : + +```json +{ + "alg": "HS256", + "typ": "JWT", + "kid": "' UNION SELECT 'secret' FROM tokens WHERE '1'='1" +} +``` + +### Variante par traversée de chemin + +L'attaquant définit `kid` sur `/dev/null` et re-signe le token avec un secret HMAC vide. Si le serveur lit la clé de signature depuis le chemin indiqué par `kid`, il reçoit zéro octet : + +```json +{ + "alg": "HS256", + "typ": "JWT", + "kid": "/dev/null" +} +``` + +## Comment tester ? + + + +```bash +vulnapi scan curl [url] -H "Authorization: Bearer eyJhbGciOiJIUzI1NiI..." --scans jwt.kid_injection +``` + + +```bash +echo "eyJhbGciOiJIUzI1NiI..." | vulnapi scan openapi [OpenAPI_Path_Or_URL] --scans jwt.kid_injection +``` + + +```bash +vulnapi scan graphql -H "Authorization: Bearer eyJhbGciOiJIUzI1NiI..." --scans jwt.kid_injection [url] +``` + + + +VulnAPI prend aussi en charge le scan d'autres types de [vulnérabilités](../). + +## Quel est l'impact ? + +Une attaque par injection KID réussie permet à un attaquant de forger un JWT que le serveur accepte comme légitime. Cela peut mener à un contournement complet de l'authentification, une élévation de privilèges, ou l'usurpation de l'identité de n'importe quel utilisateur du système. + +## Comment corriger ? + +- **Valider la valeur `kid`** avant de l'utiliser — rejeter les valeurs contenant des métacaractères SQL, des séparateurs de chemin, ou des caractères en dehors d'une liste blanche sûre (ex. alphanumérique et tirets uniquement). +- **Utiliser des requêtes paramétrées** lors de la recherche de clés par `kid` dans une base de données pour prévenir l'injection SQL. +- **Ne jamais lire le matériel de clé depuis des chemins de fichiers contrôlés par l'utilisateur** — stockez les clés dans un coffre-fort sécurisé et n'utilisez `kid` que comme identifiant opaque associé côté serveur à une clé connue. +- **Coder en dur ou mettre en liste blanche les valeurs `kid` autorisées** lorsque c'est possible. diff --git a/docs/fr/vulnerabilities/broken-authentication/jwt-not-verified.mdx b/docs/fr/vulnerabilities/broken-authentication/jwt-not-verified.mdx new file mode 100644 index 00000000..da564f34 --- /dev/null +++ b/docs/fr/vulnerabilities/broken-authentication/jwt-not-verified.mdx @@ -0,0 +1,108 @@ +--- +title: Signature JWT non vérifiée +description: Une vulnérabilité où le serveur accepte des JWT sans vérifier leur signature cryptographique, permettant aux attaquants de forger des tokens arbitraires. +--- + +import { Tabs, TabItem } from '@/components/docs' + + + + + + + + + + + + + + + + + + + + + + +
Identifiant de scanjwt.not_verified
GravitéÉlevée
CVSS 4.09.3 — CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N
Classifications + CWE-345: Insufficient Verification of Data Authenticity +
Catégorie OWASP + OWASP API2:2023 Broken Authentication +
+ +La vulnérabilité « Signature JWT non vérifiée » survient lorsqu'un serveur accepte un JWT dont la signature a été créée avec une clé complètement différente (aléatoire). Si le serveur renvoie la même réponse de succès que pour un token légitimement signé, il ne vérifie pas du tout la signature — ce qui signifie qu'un attaquant peut fabriquer n'importe quel payload et le serveur l'acceptera. + +Pour plus de détails, consultez la [documentation jwtop sur la signature non vérifiée](https://www.cerberauth.com/docs/jwtop/vulnerabilities/signature-not-verified/). + +## Exemple + +VulnAPI prend votre token valide, en extrait l'algorithme et les claims, puis le re-signe avec une clé générée aléatoirement : + +**Token valide d'origine** (signé avec la vraie clé privée) : +``` +eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIiwicm9sZSI6InVzZXIifQ. +``` + +**Token d'attaque** (mêmes claims, signé avec une clé aléatoire) : +``` +eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIiwicm9sZSI6InVzZXIifQ. +``` + +Si les deux requêtes renvoient le même code de statut HTTP (ex. 200 OK), le serveur ne vérifie pas la signature. + +## Comment tester ? + + + +```bash +vulnapi scan curl [url] -H "Authorization: Bearer eyJhbGciOiJSUzI1NiI..." --scans jwt.not_verified +``` + + +```bash +echo "eyJhbGciOiJSUzI1NiI..." | vulnapi scan openapi [OpenAPI_Path_Or_URL] --scans jwt.not_verified +``` + + +```bash +vulnapi scan graphql -H "Authorization: Bearer eyJhbGciOiJSUzI1NiI..." --scans jwt.not_verified [url] +``` + + + +VulnAPI prend aussi en charge le scan d'autres types de [vulnérabilités](../). + +## Quel est l'impact ? + +Si un attaquant peut forger des JWT arbitraires sans avoir besoin de la clé de signature, il peut : + +- **Élever ses privilèges** : modifier le claim `role` ou `permissions` pour obtenir un accès admin +- **Usurper l'identité de n'importe quel utilisateur** : changer le claim `sub` ou `user_id` vers n'importe quelle valeur +- **Contourner tous les contrôles d'accès basés sur JWT** : l'ensemble du système d'authentification est effectivement désactivé +- **Accéder à n'importe quelle ressource** : l'attaquant peut prétendre être n'importe quel utilisateur, y compris ceux dont il n'a pas les identifiants + +Le score CVSS 9.3 reflète le fait qu'il s'agit d'une faille exploitable réseau, sans authentification préalable requise, donnant un accès complet en lecture/écriture à toutes les données utilisateur. + +## Comment corriger ? + +1. **Toujours vérifier les signatures** : utilisez une bibliothèque JWT fiable et bien maintenue, et appelez la fonction de vérification — jamais un simple décodage. + +2. **Fournir la bonne clé** : transmettez explicitement la clé publique attendue (pour RS/ES) ou le secret (pour HS) ; ne laissez jamais la bibliothèque auto-détecter la clé depuis l'en-tête du token. + +3. **Restreindre les algorithmes autorisés** : configurez votre bibliothèque pour n'accepter qu'un ensemble spécifique d'algorithmes (ex. `["RS256"]`). Cela empêche les attaques de confusion d'algorithme en plus du contournement de signature. + +4. **Utiliser des algorithmes asymétriques pour les systèmes distribués** : RS256 et ES256 permettent à n'importe quel service de vérifier les tokens avec la clé publique, tandis que seul l'émetteur détient la clé privée. + +```go +// Exemple Go — toujours passer les algorithmes autorisés +token, err := jwt.Parse(tokenString, keyFunc, + jwt.WithValidMethods([]string{"RS256"}), +) +``` + +```python +# Exemple Python — toujours passer algorithms et verify=True (par défaut) +payload = jwt.decode(token, public_key, algorithms=["RS256"], audience="my-service") +``` diff --git a/docs/fr/vulnerabilities/broken-authentication/jwt-null-signature.mdx b/docs/fr/vulnerabilities/broken-authentication/jwt-null-signature.mdx new file mode 100644 index 00000000..4ac6ebe2 --- /dev/null +++ b/docs/fr/vulnerabilities/broken-authentication/jwt-null-signature.mdx @@ -0,0 +1,107 @@ +--- +title: Signature JWT nulle +description: La vulnérabilité de signature JWT nulle survient lorsqu'un JSON Web Token (JWT) est dépourvu de partie signature, permettant aux attaquants de manipuler le contenu du token. +--- + +import { Tabs, TabItem } from '@/components/docs' + + + + + + + + + + + + + + + + + + +
GravitéÉlevée
CVEs + CVE-2020-28042 +
Classifications + CWE-327: Use of a Broken or Risky Cryptographic Algorithm +
Catégorie OWASP + OWASP API2:2023 Broken Authentication +
+ +La vulnérabilité « Signature JWT nulle » survient lorsqu'un JSON Web Token (JWT) est dépourvu de partie signature, permettant aux attaquants de manipuler le contenu du token, ce qui peut mener à un accès non autorisé et à une falsification des données. + +Pour plus de détails, consultez la [documentation jwtop sur la signature nulle](https://www.cerberauth.com/docs/jwtop/vulnerabilities/null-signature/). + +Cette vulnérabilité est similaire à celle du « secret JWT vide », mais dans ce cas, le token est dépourvu de partie signature, ce qui facilite encore davantage la manipulation de son contenu par un attaquant. + +## Exemple + +Voici un JWT valide signé avec l'algorithme RS512 : + +```bash +eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.MnECRBSUQEi8GjiAyWHPhPhhpzCiMLldkq-N_VS-iwI08c4xEVUhT1Xrx9kNGuwusiQuLI3AOBTPwtbdaasQDCOpF0nxxQNKkufJYFds61ooFZfXCuRyXe1yGnXPRzTfgr5YVe9-T8_JDccx5JP70d9hoO4DU4GNYQMvrOQl4xu8DEyyDT2hsjyTbrodVhrV9znMfEBCsYPPLI-Q-HYLquGThPdJe2kBNA-CiLRV6Mwzji67cTd_4P_oUHKXsAxMqVpo-xC2xiVpO2P9X1__uXrRrfiNFUur4B71UMgGYJ2z_cQqwFfSXz9glBIf_-BJU10Rkmyo2ew862d7WsHx8g +``` + +Ce JWT décodé contient ces parties : + +```json +{ + "alg": "RS512", + "typ": "JWT" +} +``` + +```json +{ + "sub": "1234567890", + "iat": 1516239022 +} +``` + +Le JWT suivant a sa signature retirée (attaque par signature nulle) : + +```bash +eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjkxNTU4MDksImlhdCI6MTcyOTE1MjIwOSwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0. +``` + +## Comment tester ? + +Si vous voulez tester uniquement la vulnérabilité « Signature JWT nulle », vous pouvez utiliser la commande suivante : + + + +```bash +vulnapi scan curl [url] -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans jwt.null_signature +``` + + +```bash +echo "eyJhbGciOiJSUzUxMiI..." | vulnapi scan openapi [OpenAPI_Path_Or_URL] --scans jwt.null_signature +``` + + +```bash +vulnapi scan graphql -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans jwt.null_signature [url] +``` + + + +VulnAPI prend aussi en charge le scan d'autres types de [vulnérabilités](../). + +## Quel est l'impact ? + +Signer un JWT avec une signature nulle a un impact significatif sur la sécurité du token. Une signature nulle signifie qu'aucune partie signature n'est utilisée pour signer le token, ce qui le rend vulnérable à la falsification et à l'accès non autorisé. + +En manipulant le contenu du token, les attaquants peuvent obtenir un accès non autorisé à des données sensibles, usurper l'identité d'utilisateurs, et mener d'autres activités malveillantes. + +## Comment corriger ? + +Pour corriger la vulnérabilité « Signature JWT nulle », vous devez vous assurer que tous les JWT sont vérifiés avec une signature valide avant d'être traités. + +Voici quelques bonnes pratiques à suivre : +- Utilisez toujours un algorithme cryptographique robuste comme HS512, RS512, ou EdDSA pour signer les JWT. +- Assurez-vous que la clé secrète utilisée pour signer les JWT est conservée de façon sécurisée et non exposée à des utilisateurs non autorisés. +- Mettez en place une validation et une désinfection appropriées des entrées pour empêcher les attaquants d'injecter du contenu malveillant dans les JWT. +- Surveillez et auditez régulièrement les JWT pour détecter toute tentative d'accès non autorisé ou de falsification. diff --git a/docs/fr/vulnerabilities/broken-authentication/jwt-weak-secret.mdx b/docs/fr/vulnerabilities/broken-authentication/jwt-weak-secret.mdx new file mode 100644 index 00000000..26f5cecd --- /dev/null +++ b/docs/fr/vulnerabilities/broken-authentication/jwt-weak-secret.mdx @@ -0,0 +1,108 @@ +--- +title: Secret JWT faible +description: Une vulnérabilité survient lorsqu'un JSON Web Token (JWT) est signé avec un secret commun, bien connu, ou faible. Dans ce scénario, le token manque d'une protection cryptographique adéquate, le rendant susceptible d'être manipulé. +--- + +import { Tabs, TabItem } from '@/components/docs' + + + + + + + + + + + + + + + + + + +
GravitéÉlevée
CVEs + * [CVE-2023-27172](https://nvd.nist.gov/vuln/detail/CVE-2023-27172) + * [CVE-2023-46943](https://nvd.nist.gov/vuln/detail/CVE-2023-46943) +
Classifications + * [CWE-287: Improper Authentication](https://cwe.mitre.org/data/definitions/287.html) + * [CWE-307: Improper Restriction of Excessive Authentication Attempts](https://cwe.mitre.org/data/definitions/307.html) + * [CWE-798: Use of Hard-coded Credentials](https://cwe.mitre.org/data/definitions/798.html) +
Catégorie OWASP + [OWASP API2:2023 Broken Authentication](https://owasp.org/API-Security/editions/2023/en/0xa2-broken-authentication/) +
+ +Une vulnérabilité survient lorsqu'un JSON Web Token (JWT) est signé avec un secret commun, bien connu, ou faible. Dans ce scénario, le token manque d'une protection cryptographique adéquate, le rendant susceptible d'être manipulé. Les attaquants peuvent trouver le secret puis modifier les claims et le contenu du token sans être détectés, pouvant mener à un accès non autorisé et à une falsification des données. + +Pour plus de détails, consultez la [documentation jwtop sur le secret faible](https://www.cerberauth.com/docs/jwtop/vulnerabilities/weak-secret/). + +## Quels sont les différents scénarios ? + +- **Secret commun** : la clé secrète utilisée pour signer le JWT est une valeur commune, telle que `secret`, `password`, ou `123456`. Les attaquants peuvent facilement deviner (par force brute) la clé secrète. +- **Secret bien connu** : la clé secrète utilisée pour signer le JWT est une valeur bien connue ou une valeur par défaut. Cela peut arriver lorsque vous utilisez une clé secrète par défaut fournie par un produit, une bibliothèque ou un framework. Les attaquants peuvent trouver la clé secrète dans des dépôts publics, des forums, ou de la documentation. +- **Secret faible** : la clé secrète utilisée pour signer le JWT est une valeur faible, telle qu'une courte chaîne devinable comme `security2024`. Les attaquants peuvent utiliser des attaques par dictionnaire, des rainbow tables, ou d'autres techniques de force brute pour trouver la clé secrète. + +## Exemple + +Voici un JWT valide signé avec l'algorithme HS256 et une clé secrète robuste : + +```bash +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MTYyNDI2MjIsImlhdCI6MTUxNjIzOTAyMiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.ZuwZrXpLRj17vDjOLoOOJ7pr1CN5DnE8Clgn4y-fjNs +``` + +Ce JWT décodé contient ces parties : + +```json +{ + "alg": "HS256", + "typ": "JWT" +} +``` + +```json +{ + "iat": 1516239022, + "exp": 1516242622, + "name": "John Doe", + "sub": "2cb307ba-bb46-4194-854f-4774046d9c9b" +} +``` + +Le JWT suivant est signé avec le secret `secret` : + +```bash +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MTYyNDI2MjIsImlhdCI6MTUxNjIzOTAyMiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.gTgBr6lotpAxs4M46PgUXrjhIN5-gYG4HffKSEIB6Ys +``` + +## Comment tester ? + +Si vous voulez tester uniquement la vulnérabilité « Secret JWT faible », vous pouvez utiliser la commande suivante : + + + +```bash +vulnapi scan curl [url] -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans jwt.weak_secret +``` + + +```bash +echo "eyJhbGciOiJSUzUxMiI..." | vulnapi scan openapi [OpenAPI_Path_Or_URL] --scans jwt.weak_secret +``` + + +```bash +vulnapi scan graphql -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans jwt.weak_secret [url] +``` + + + +VulnAPI prend aussi en charge le scan d'autres types de vulnérabilités JWT. + +## Quel est l'impact ? + +L'impact de l'utilisation d'une clé secrète faible pour signer un JWT est significatif. Les attaquants peuvent facilement trouver la clé secrète et modifier les claims et le contenu du token sans être détectés. Cela peut mener à un accès non autorisé, une falsification des données, et d'autres risques de sécurité. + +## Comment corriger ? + +Assurez-vous de changer la clé secrète pour une valeur forte et unique. Utilisez un générateur aléatoire sécurisé pour créer la clé secrète et stockez-la de façon sécurisée. Faites tourner la clé secrète périodiquement pour atténuer le risque d'accès non autorisé et de falsification des données. diff --git a/docs/fr/vulnerabilities/security-misconfiguration/_meta.yml b/docs/fr/vulnerabilities/security-misconfiguration/_meta.yml new file mode 100644 index 00000000..eac747d9 --- /dev/null +++ b/docs/fr/vulnerabilities/security-misconfiguration/_meta.yml @@ -0,0 +1 @@ +label: Mauvaise configuration de sécurité diff --git a/docs/fr/vulnerabilities/security-misconfiguration/graphql-introspection.mdx b/docs/fr/vulnerabilities/security-misconfiguration/graphql-introspection.mdx new file mode 100644 index 00000000..16ec72e2 --- /dev/null +++ b/docs/fr/vulnerabilities/security-misconfiguration/graphql-introspection.mdx @@ -0,0 +1,56 @@ +--- +title: Introspection GraphQL activée +description: L'introspection GraphQL est une fonctionnalité permettant aux clients d'interroger le schéma du serveur. Elle peut être utilisée pour découvrir le schéma et les types, mais aussi par des attaquants pour en apprendre davantage sur les détails d'implémentation du serveur et trouver des vulnérabilités potentielles. +--- + + + + + + + + + + + + + + + + + + +
GravitéFaible
CVEs
Classifications + CWE-200: Information Exposure +
Catégorie OWASP + OWASP API8:2023 Security Misconfiguration +
+ +L'introspection GraphQL est une fonctionnalité permettant aux clients d'interroger le schéma du serveur. Elle peut être utilisée pour découvrir le schéma et les types, mais aussi par des attaquants pour en apprendre davantage sur les détails d'implémentation du serveur et trouver des vulnérabilités potentielles. + +## Quel est l'impact ? + +Les impacts potentiels de l'introspection GraphQL sur la sécurité sont significatifs et peuvent inclure : +* **Divulgation d'informations** : les attaquants peuvent utiliser l'introspection pour en apprendre davantage sur les détails d'implémentation du serveur, tels que les types, champs, et arguments, ce qui peut mener à une divulgation d'informations et des vulnérabilités de sécurité potentielles. +* **Énumération du schéma** : l'introspection peut être utilisée pour énumérer le schéma et découvrir des champs cachés ou dépréciés, qui peuvent être exploités pour fabriquer des attaques ciblées ou exploiter des vulnérabilités. +* **Mauvaise configuration de sécurité** : une introspection mal configurée peut exposer des informations sensibles ou des détails d'implémentation internes, menant à des mauvaises configurations de sécurité et des risques potentiels. +* **Exposition de données** : l'introspection peut révéler des structures de données sensibles, telles que des tables de base de données, des champs, ou des relations, qui peuvent être exploitées pour accéder à des données ou les manipuler de façon non autorisée. + +## Comment tester ? + +Si vous voulez tester uniquement la vulnérabilité « Introspection GraphQL activée », vous pouvez utiliser la commande suivante : + +```bash +vulnapi scan graphql [url] --scans graphql.introspection_enabled +``` + +## Comment corriger ? + +Pour corriger les vulnérabilités d'introspection GraphQL, vous pouvez suivre les étapes suivantes : +* **Désactiver l'introspection** : désactivez l'introspection dans les environnements de production pour empêcher les attaquants d'interroger le schéma et de découvrir des informations sensibles ou des vulnérabilités de sécurité potentielles. +* **Limiter l'accès** : restreignez l'accès aux requêtes d'introspection aux utilisateurs ou applications autorisés, et assurez-vous que seules des entités de confiance peuvent interroger le schéma et accéder aux détails d'implémentation internes. + +## Références + +- [GraphQL Introspection](https://graphql.org/learn/introspection/) +- [PortSwigger - GraphQL Introspection enabled](https://portswigger.net/kb/issues/00200512_graphql-introspection-enabled) diff --git a/docs/fr/vulnerabilities/security-misconfiguration/http-cookies.mdx b/docs/fr/vulnerabilities/security-misconfiguration/http-cookies.mdx new file mode 100644 index 00000000..8d945876 --- /dev/null +++ b/docs/fr/vulnerabilities/security-misconfiguration/http-cookies.mdx @@ -0,0 +1,127 @@ +--- +title: Mauvaise configuration des cookies HTTP +description: Des indicateurs de sécurité de cookies manquants ou mal configurés exposent les tokens de session au vol via XSS, l'interception réseau, et la falsification de requête cross-site. +--- + +import { Tabs, TabItem } from '@/components/docs' + + + + + + + + + + + + + + + + + + +
Identifiant de scanmisconfiguration.http_cookies
GravitéInfo
CVSS 4.00.0
Catégorie OWASP + OWASP API8:2023 Security Misconfiguration +
+ +Les indicateurs de sécurité des cookies protègent les tokens de session et cookies d'authentification du vol et des usages abusifs. Des indicateurs manquants ou mal configurés représentent individuellement des risques de faible gravité, mais combinés, ils augmentent significativement le risque de détournement de session, de vol de token via XSS, et d'attaques cross-site. + +## Contrôles effectués + +Le scan `misconfiguration.http_cookies` de VulnAPI vérifie cinq problèmes sur chaque en-tête de réponse `Set-Cookie` : + +### Indicateur `HttpOnly` manquant (CWE-1004) + +Sans `HttpOnly`, le JavaScript exécuté sur la page (y compris les scripts injectés via XSS) peut lire la valeur du cookie via `document.cookie`. + +``` +Set-Cookie: session=abc123 ← vulnérable +Set-Cookie: session=abc123; HttpOnly ← correct +``` + +### Indicateur `Secure` manquant (CWE-614) + +Sans `Secure`, le cookie est envoyé sur des connexions HTTP non chiffrées, l'exposant à l'interception réseau (attaques de type man-in-the-middle). + +``` +Set-Cookie: session=abc123 ← vulnérable +Set-Cookie: session=abc123; Secure ← correct +``` + +### `SameSite=None` sans restrictions (CWE-1275) + +`SameSite=None` permet l'envoi du cookie dans tous les contextes cross-site, ce qui peut permettre des attaques CSRF si `Secure` n'est pas également défini. Utilisez plutôt `SameSite=Strict` ou `SameSite=Lax`. + +``` +Set-Cookie: session=abc123; SameSite=None ← vulnérable +Set-Cookie: session=abc123; SameSite=Strict; Secure ← correct +``` + +### Attribut `SameSite` non défini (CWE-1275) + +Sans attribut `SameSite`, les navigateurs appliquent la politique par défaut (Lax dans les navigateurs modernes, mais None dans les plus anciens). Définissez explicitement `SameSite` pour ne pas dépendre des valeurs par défaut du navigateur. + +### Absence d'expiration (`Expires` / `Max-Age` manquant) (CWE-613) + +Sans expiration, le cookie est un cookie de session — il n'expire que lorsque l'onglet du navigateur se ferme. Les utilisateurs qui ne ferment pas leur navigateur peuvent conserver l'accès à la session indéfiniment. + +``` +Set-Cookie: session=abc123 ← pas d'expiration +Set-Cookie: session=abc123; Max-Age=3600 ← expire après 1 heure +``` + +## Comment tester ? + + + +```bash +vulnapi scan curl [url] -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans misconfiguration.http_cookies +``` + + +```bash +echo "eyJhbGciOiJSUzUxMiI..." | vulnapi scan openapi [OpenAPI_Path_Or_URL] --scans misconfiguration.http_cookies +``` + + +```bash +vulnapi scan graphql -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans misconfiguration.http_cookies [url] +``` + + + +VulnAPI prend aussi en charge le scan d'autres types de [vulnérabilités](../). + +## Quel est l'impact ? + +Les indicateurs de cookie manquants représentent individuellement des résultats de faible gravité, mais combinés, ils créent une surface d'attaque significative : + +- **XSS + `HttpOnly` manquant** → l'attaquant peut voler le token de session via `document.cookie` +- **`Secure` manquant** → token exposé en HTTP (ex. pages en contenu mixte, portails captifs) +- **`SameSite` manquant** → les attaques CSRF peuvent utiliser le cookie de session cross-origin +- **Absence d'expiration** → des sessions de longue durée augmentent la fenêtre d'opportunité pour les abus + +## Comment corriger ? + +Définissez tous les indicateurs de sécurité sur les cookies transportant des tokens de session ou des données d'authentification : + +```http +Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Strict; Max-Age=3600; Path=/ +``` + +| Indicateur | Valeur | Objectif | +|------|-------|---------| +| `HttpOnly` | (indicateur) | Empêcher l'accès JavaScript | +| `Secure` | (indicateur) | HTTPS uniquement | +| `SameSite` | `Strict` ou `Lax` | Restreindre l'envoi cross-site | +| `Max-Age` | secondes | Définir la durée de vie de la session | +| `Path` | `/` ou chemin spécifique | Restreindre la portée du cookie | + +Pour les applications monopages nécessitant des cookies cross-origin (ex. domaines API et frontend séparés), utilisez `SameSite=None; Secure` accompagné de tokens CSRF. + +## Références + +- [OWASP Secure Cookie Attributes](https://owasp.org/www-community/controls/SecureCookieAttribute) +- [MDN Set-Cookie](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) diff --git a/docs/fr/vulnerabilities/security-misconfiguration/http-method-allow-override.mdx b/docs/fr/vulnerabilities/security-misconfiguration/http-method-allow-override.mdx new file mode 100644 index 00000000..cbed9087 --- /dev/null +++ b/docs/fr/vulnerabilities/security-misconfiguration/http-method-allow-override.mdx @@ -0,0 +1,79 @@ +--- +title: Substitution de méthode HTTP activée +description: La substitution de méthode HTTP est une fonctionnalité permettant aux clients de remplacer la méthode HTTP par défaut utilisée dans une requête. Cette fonctionnalité peut être exploitée par des attaquants pour contourner les contrôles de sécurité et effectuer des actions non autorisées sur le serveur. +--- + +import { Tabs, TabItem } from '@/components/docs' + + + + + + + + + + + + + + + + + + +
GravitéInfo - Élevée
CVEs + +
Classifications + CWE-287: Improper Authentication +
Catégorie OWASP + OWASP API8:2023 Security Misconfiguration +
+ +La substitution de méthode HTTP est une fonctionnalité permettant aux clients de remplacer la méthode HTTP par défaut utilisée dans une requête. Cette fonctionnalité est couramment utilisée pour effectuer des actions sur le serveur avec des méthodes HTTP autres que GET et POST, telles que PUT, DELETE, PATCH, etc. Elle est généralement implémentée via des en-têtes personnalisés ou des paramètres de requête spécifiant la méthode HTTP souhaitée. + +Cette fonctionnalité peut être exploitée par des attaquants pour contourner les contrôles de sécurité et effectuer des actions non autorisées sur le serveur. + +## Quel est l'impact ? + +Les attaquants peuvent exploiter cette fonctionnalité pour contourner les contrôles de sécurité et effectuer des actions non autorisées sur le serveur. Voici quelques attaques courantes pouvant être menées via la substitution de méthode HTTP : +- Attaques CSRF +- Contournement de l'authentification +- Contournement des contrôles d'accès + +## Comment tester ? + +Si vous voulez tester uniquement les problèmes de « Substitution de méthode HTTP activée », vous pouvez utiliser la commande suivante : + + + +```bash +vulnapi scan curl [url] --scans misconfiguration.http_method_override +``` + + +```bash +vulnapi scan openapi [OpenAPI_Path_Or_URL] --scans misconfiguration.http_method_override +``` + + +```bash +vulnapi scan graphql --scans misconfiguration.http_method_override [url] +``` + + + +## Comment corriger ? + +Pour corriger ce problème, vous devez désactiver la fonctionnalité de substitution de méthode HTTP sur l'API ou le proxy intermédiaire. Vous pouvez le faire en configurant le serveur pour n'accepter que les méthodes HTTP attendues (GET, POST, PUT, DELETE, etc.) et rejeter toute autre méthode non explicitement autorisée. + +Si vous ne pouvez pas désactiver ce comportement, assurez-vous de mettre en place des contrôles d'accès et des vérifications de validation adéquats pour empêcher les actions non autorisées, et que ces contrôles ne sont pas impactés par la méthode substituée. Pour ce faire, la pratique habituelle consiste à effectuer les vérifications avant que la substitution de méthode ne soit appliquée. + +## Références + +- [X-HTTP-Method](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-odata/bdbabfa6-8c4a-4741-85a9-8d93ffd66c41) +- [HTTP Method Override Blog Post](https://www.sidechannel.blog/en/http-method-override-what-it-is-and-how-a-pentester-can-use-it/) diff --git a/docs/fr/vulnerabilities/security-misconfiguration/http-trace-track.mdx b/docs/fr/vulnerabilities/security-misconfiguration/http-trace-track.mdx new file mode 100644 index 00000000..f91faeda --- /dev/null +++ b/docs/fr/vulnerabilities/security-misconfiguration/http-trace-track.mdx @@ -0,0 +1,123 @@ +--- +title: Méthodes HTTP TRACE et TRACK activées +description: Les méthodes HTTP TRACE et TRACK renvoient la requête entrante en écho, exposant potentiellement les en-têtes d'autorisation aux attaquants. +--- + +import { Tabs, TabItem } from '@/components/docs' + + + + + + + + + + + + + + + + + + +
Identifiants de scanmisconfiguration.http_trace, misconfiguration.http_track
GravitéInfo
Classifications + CWE-489: Active Debug Code +
Catégorie OWASP + OWASP API8:2023 Security Misconfiguration +
+ +Les méthodes HTTP `TRACE` et `TRACK` sont des méthodes de débogage qui demandent au serveur web de renvoyer en écho la requête entrante complète — y compris tous les en-têtes — dans le corps de la réponse. Cela signifie que tout en-tête `Authorization`, `Cookie`, ou personnalisé envoyé avec la requête est renvoyé tel quel dans la réponse. + +Dans une attaque de Cross-Site Tracing (XST), un script exécuté dans un navigateur pouvant émettre des requêtes TRACE pourrait utiliser ce comportement d'écho pour voler des cookies `HttpOnly` ou des en-têtes d'autorisation normalement inaccessibles à JavaScript. Bien que les politiques de sécurité des navigateurs modernes (CSP, CORS, `SameSite`) atténuent de nombreux chemins XST, laisser TRACE/TRACK activés viole le principe du moindre privilège et peut exposer des en-têtes sensibles dans des journaux, des proxies, ou des environnements moins protégés. + +## Exemple + +```http +TRACE /api/users HTTP/1.1 +Host: api.example.com +Authorization: Bearer eyJhbGciOiJSUzI1NiI... +X-Custom-Internal: secret + +HTTP/1.1 200 OK +Content-Type: message/http + +TRACE /api/users HTTP/1.1 +Host: api.example.com +Authorization: Bearer eyJhbGciOiJSUzI1NiI... +X-Custom-Internal: secret +``` + +La requête complète — y compris `Authorization` — est reflétée dans le corps de la réponse. + +## Comment tester ? + + + +```bash +vulnapi scan curl [url] -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans misconfiguration.http_trace --scans misconfiguration.http_track +``` + + +```bash +echo "eyJhbGciOiJSUzUxMiI..." | vulnapi scan openapi [OpenAPI_Path_Or_URL] --scans misconfiguration.http_trace --scans misconfiguration.http_track +``` + + +```bash +vulnapi scan graphql -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans misconfiguration.http_trace --scans misconfiguration.http_track [url] +``` + + + +VulnAPI prend aussi en charge le scan d'autres types de [vulnérabilités](../). + +## Quel est l'impact ? + +- **Exposition d'en-têtes** : `Authorization`, `Cookie`, et les en-têtes internes personnalisés envoyés avec une requête TRACE sont reflétés dans le corps de la réponse, les rendant lisibles par toute partie pouvant observer la réponse (proxies intermédiaires, journaux, scripts de navigateur dans certaines conditions). +- **Fuite d'informations de débogage** : des en-têtes internes (`X-Internal-User`, `X-Request-ID`, etc.) normalement invisibles aux clients peuvent être exposés. +- **Cross-Site Tracing (XST)** : dans des environnements de navigateur plus anciens ou combiné à d'autres vulnérabilités, TRACE peut être utilisé pour voler des identifiants depuis des clients basés navigateur. + +## Comment corriger ? + +Désactivez TRACE et TRACK au niveau du serveur web. + +**Nginx** — restreindre aux méthodes autorisées uniquement : + +```nginx +location / { + limit_except GET POST PUT DELETE PATCH OPTIONS { + deny all; + } +} +``` + +**Apache** — désactiver TRACE globalement : + +```apache +TraceEnable Off +``` + +**Express (Node.js)** — rejeter TRACE/TRACK dans un middleware : + +```js +app.use((req, res, next) => { + if (req.method === 'TRACE' || req.method === 'TRACK') { + return res.status(405).send('Method Not Allowed'); + } + next(); +}); +``` + +Vérifiez la correction en vérifiant que TRACE renvoie une réponse `405 Method Not Allowed` : + +```bash +curl -X TRACE https://api.example.com/ -i +# Attendu : HTTP/1.1 405 Method Not Allowed +``` + +## Références + +- [OWASP Testing for HTTP Methods](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/02-Configuration_and_Deployment_Management_Testing/06-Test_HTTP_Methods) +- [CWE-489: Active Debug Code](https://cwe.mitre.org/data/definitions/489.html) diff --git a/docs/fr/vulnerabilities/security-misconfiguration/tls.mdx b/docs/fr/vulnerabilities/security-misconfiguration/tls.mdx new file mode 100644 index 00000000..a5fcc209 --- /dev/null +++ b/docs/fr/vulnerabilities/security-misconfiguration/tls.mdx @@ -0,0 +1,71 @@ +--- +title: TLS manquant ou mal configuré +description: TLS (Transport Layer Security) est un protocole assurant une communication sécurisée. Un TLS manquant ou mal configuré pour une API peut exposer des données sensibles aux attaquants. +--- + + + + + + + + + + + + + + +
GravitéInfo
CVEs + +
Catégorie OWASP + OWASP API8:2023 Security Misconfiguration +
+ +## Qu'est-ce que TLS ? + +Transport Layer Security (TLS) est un protocole assurant une communication sécurisée sur un réseau informatique. Il est largement utilisé pour sécuriser le trafic web, les e-mails, et d'autres types de communication. TLS chiffre les données transmises entre deux parties, garantissant qu'elles ne peuvent pas être lues ou falsifiées par des attaquants. Dans le contexte des API, TLS est utilisé pour sécuriser la communication entre clients et serveurs. + +## Quel est l'impact ? + +Si TLS est manquant ou mal configuré pour une API, les données sensibles transmises entre clients et serveurs peuvent être exposées aux attaquants. Cela peut mener à des fuites de données, un accès non autorisé à des informations sensibles, et d'autres risques de sécurité. Les attaquants peuvent intercepter et lire les données transmises sur le réseau, obtenant potentiellement l'accès à des informations sensibles telles que des identifiants utilisateur, des données personnelles, et d'autres informations confidentielles. + +## Vulnérabilités + +### TLS manquant + +C'est la mauvaise configuration la plus facile à détecter. Si une API n'utilise pas TLS, vous pouvez facilement le constater en regardant l'URL. Si l'URL commence par `http://` au lieu de `https://`, alors l'API n'utilise pas TLS. + +### POODLE (Padding Oracle On Downgraded Legacy Encryption) + +POODLE est une vulnérabilité affectant le protocole SSLv3. Si une API supporte SSLv3, elle est vulnérable aux attaques POODLE. + +### CRIME (Compression Ratio Info-leak Made Easy) + +CRIME est une vulnérabilité affectant TLS et SPDY utilisant la compression de données. Si une API supporte la compression de données et TLS, elle est vulnérable aux attaques CRIME pouvant divulguer des cookies. + +### Heartbleed + +Heartbleed est une vulnérabilité de la bibliothèque OpenSSL permettant aux attaquants de lire des données sensibles dans la mémoire du serveur. Si une API utilise une version vulnérable d'OpenSSL, elle est susceptible aux attaques Heartbleed. + +## Comment tester ? + +Utilisez un outil comme [SSL Labs](https://www.ssllabs.com/ssltest/) pour tester la configuration TLS d'une API. L'outil vérifiera les vulnérabilités TLS courantes et fournira une note pour la configuration TLS de l'API. + +## Comment corriger ? + +Tout d'abord, si vous n'avez pas encore ajouté TLS, vous devriez l'ajouter à votre API. Vous pouvez utiliser [Let's Encrypt](https://letsencrypt.org/) pour obtenir un certificat TLS gratuit pour votre API, ou utiliser votre hébergeur ou fournisseur cloud pour obtenir un certificat. + +Ensuite, si vous avez TLS activé et que vous avez détecté l'une des vulnérabilités mentionnées ci-dessus, vous devriez mettre à jour votre configuration TLS pour atténuer la vulnérabilité. Par exemple, si votre API supporte SSLv3, vous devriez le désactiver et ne supporter que TLS 1.2 ou supérieur. + +## Références + +- [Let's Encrypt](https://letsencrypt.org/) +- [SSL Labs](https://www.ssllabs.com/ssltest/) +- [Breach Attack](https://www.breachattack.com/)