Examen de spécialiste agréé en sécurité de l'information — Printemps 2024 (Reiwa 6), question 1 de l'après-midi expliquée — JWT alg=none, autorisation API et mesure provisoire par WAF
· Go Komura · Spécialiste agréé en sécurité de l'information, Examen RISS, API, Sécurité des API, JWT, Authentification, Autorisation, WAF, Log4Shell, Sécurité de l'information, Vulnérabilité, IPA
« Comme on vérifie la signature du JWT, on peut faire confiance à l’identifiant d’utilisateur. »
Cette affirmation n’est vraie qu’à moitié.
La question 1 de l’après-midi de l’examen de spécialiste agréé en sécurité de l’information, session printemps 2024 (Reiwa 6), prend pour sujet une API appelée depuis un smartphone1. Une fois l’authentification réussie, un JWT est émis, et ce JWT accompagne les appels aux API de récupération et de mise à jour des informations de l’utilisateur. À première vue, c’est une configuration courante.
Pourtant, l’audit révèle les quatre problèmes suivants.
- En changeant
algdans l’en-tête du JWT pournone, un JWT sans signature est accepté - En gardant un JWT valide mais en changeant
midvers un autre identifiant d’utilisateur, on peut récupérer et modifier les informations d’autrui - En ajoutant
status=paid, absent de la spécification, un utilisateur gratuit devient un utilisateur payant - Le code d’authentification à 4 chiffres reçu par e-mail peut être attaqué par force brute sans aucune limite
Ces quatre problèmes ont tous l’air d’être des « vulnérabilités liées à l’authentification », mais leur cause n’est pas la même. Ce sont des frontières distinctes qui sont franchies : l’intégrité du jeton, l’autorisation au niveau de l’objet, l’autorisation au niveau de la propriété, et la limitation du nombre de tentatives.
Dans la seconde moitié, un autre sujet s’ajoute. Une vulnérabilité critique est publiée dans une bibliothèque open source largement utilisée, permettant d’exécuter du code depuis l’extérieur en exploitant JNDI Lookup. Ni version corrigée ni règle WAF finalisée n’existent encore. Pendant ce temps, il faut déterminer comment vérifier l’impact, où le WAF doit regarder, et pourquoi on commence par le mode « détection » plutôt que « blocage ».
Cet article s’appuie sur les exemples de réponses officiels2 et le commentaire de notation3 pour organiser, pour chaque question, non seulement la réponse, mais aussi pourquoi c’est cette réponse-là, et jusqu’à quel niveau de rigueur la concevoir en pratique.
flowchart TB
accTitle: Vue d'ensemble du problème
accDescr: Montre les frontières de confiance franchies à chaque étape : code d'authentification, JWT, autorisation API, vulnérabilité de bibliothèque
user[Application de l'utilisateur]
auth[Code d'authentification à 4 chiffres]
jwt[Émission du JWT]
api[API utilisateur]
log[Sortie de journal]
vuln[Bibliothèque vulnérable]
ext[Exécution de code externe]
user --> auth
auth -->|Aucune limite de tentatives| jwt
jwt -->|Autorise alg=none| api
api -->|Fait confiance à mid| db1[Récupération/modification des données d'autrui]
api -->|status=paid| db2[Modification de l'état de facturation]
api --> log
log --> vuln
vuln -->|JNDI/LDAP/HTTP| ext
Figure 1 : vue d’ensemble du problème. Une frontière de confiance différente est franchie à chaque étape.
1. La conclusion, d’abord
- Le fait qu’une API RESTful n’ait pas de session s’appelle être sans état (stateless). Cela ne signifie toutefois pas que le serveur ne conserve absolument aucune base de données ni aucun état d’utilisateur
- Un code d’authentification à 4 chiffres compte 10 000 combinaisons. À 10 tentatives par seconde, cela réussit en moyenne au bout de 5 000 tentatives, soit 500 secondes. C’est plus court que la durée de validité de 10 minutes, donc la seule durée d’expiration ne suffit pas à protéger
- La mesure minimale contre
alg=noneconsiste à vérifier quealgdans l’en-tête du JWT n’est pasNONE. En pratique, cependant, on fixe côté serveur les algorithmes autorisés - Même si le JWT est correct, il ne faut pas faire confiance au
midde la requête. Il faut soit comparer l’identifiant d’utilisateur du JWT avecmid, soit, de façon plus sûre, ne pas recevoirmiddu tout et déterminer la cible à partir du JWT - L’ajout de
status=paidest un problème de Mass Assignment, qui relie jusqu’à l’objet interne des propriétés hors spécification. Utiliser le DTO de mise à jour comme liste d’autorisation, et ne jamais laisser l’utilisateur modifier l’état de facturation - La réponse attendue contre la force brute est un traitement qui verrouille le compte lorsque le nombre d’échecs consécutifs dépasse un seuil. En pratique, on ajoute aussi un délai progressif et un contrôle par source
- Pour vérifier l’impact d’une nouvelle vulnérabilité critique, on n’utilise pas de commande destructrice : on enregistre l’accès à
index.htmld’un serveur de test pour confirmer que l’exécution de code externe est bien atteinte - La chaîne d’attaque se trouve dans un en-tête HTTP, donc la cible d’inspection du WAF est
Header. Comme expression régulière tolérant l’inversion majuscules/minuscules, on peut par exemple utiliser\W[jJ][nN][dD][iI]\W - L’avantage de mettre d’abord le WAF en mode « détection » est d’éviter de bloquer le trafic professionnel à cause d’un faux positif. À chaque alerte reçue, on examine s’il s’agit réellement d’une attaque, puis on passe au blocage après réglage
- Le WAF est une mesure provisoire ; la mesure de fond est la mise à jour de la bibliothèque concernée vers une version corrigée
2. Le sujet et la correspondance des questions
Le contexte du problème est l’entreprise G, qui cherche à lancer un nouveau service de santé. Les utilisateurs saisissent depuis une application mobile des données comme leurs repas et leur poids, et reçoivent en retour une évaluation du risque pour la santé et des conseils de menus. Le système est construit sur le cloud, combinant une passerelle API, un traitement piloté par événements et une base de données managée.
Les noms de produits et de services de l’énoncé sont rendus abstraits. Cet article, lui aussi, ne reproduit pas les figures et le texte de l’IPA, et ne reformule que la structure nécessaire à la compréhension des questions.
| Question | Sujet | Chapitre de cet article |
|---|---|---|
| Question 1 | Nature d’une API RESTful | Chapitre 4 |
| Question 2(1) | Temps de force brute sur le code à 4 chiffres | Chapitre 5 |
| Question 2(2) | alg=none du JWT |
Chapitre 6 |
| Question 2(3) | Accès à autrui via mid |
Chapitre 7 |
| Question 2(4) | Faille acceptant un status hors spécification |
Chapitre 8 |
| Question 2(5) | Mesure contre la force brute | Chapitre 9 |
| Question 3(1) | Confirmer l’existence de la vulnérabilité de façon sûre | Chapitre 11 |
| Question 3(2)(3) | Emplacement à surveiller par le WAF et expression régulière | Chapitre 12 |
| Question 3(4) | Avantage du mode détection et exploitation | Chapitre 13 |
Le commentaire de notation indique que le taux de bonnes réponses global était dans la moyenne. En revanche, il relève que le taux de bonnes réponses était relativement faible pour la mesure contre la falsification du JWT (question 2(2)) et pour le mécanisme nécessaire au serveur de vérification (question 3(1)). Dans les deux cas, connaître seulement les termes ne suffit pas à résoudre la question. Il faut suivre quelle valeur l’attaquant a modifiée, vers quel traitement elle circule, et où elle a fini par être considérée comme digne de confiance.
3. Ce problème n’est pas « un simple problème d’authentification »
En alignant l’ensemble du problème par frontière de confiance, on obtient ce qui suit.
[Identifiant utilisateur / mot de passe]
|
v
[Vérification du code à 4 chiffres] ---- pas de limite de tentatives ----> force brute
|
v
[Émission du JWT]
|
v
[Bibliothèque JWT] ------- autorise alg=none ------> falsification de l'identifiant utilisateur
|
v
[API utilisateur]
| |
| +-- transmet status en bloc ----> défaut d'autorisation au niveau de la propriété
|
+-- fait confiance à mid ------------------------> défaut d'autorisation au niveau de l'objet
[Enregistrement de l'entrée externe dans le journal]
|
v
[Bibliothèque vulnérable] ---- JNDI/LDAP/HTTP ------> exécution de code externe
Ici, la distinction la plus importante est la suivante.
| Vérification | Ce qu’elle questionne | Exemple franchi dans ce problème |
|---|---|---|
| Authentification | Qui êtes-vous ? | Force brute sur le code à 4 chiffres |
| Validation du jeton | Ces informations d’identité ont-elles été falsifiées ? | alg=none |
| Autorisation au niveau de l’objet | Êtes-vous autorisé à accéder aux données de cet utilisateur ? | Substitution de mid |
| Autorisation au niveau de la propriété | Êtes-vous autorisé à modifier ce champ ? | status=paid |
| Frontière entrée-exécution | Une entrée externe est-elle interprétée comme une instruction ? | JNDI Lookup |
Réussir la vérification précédente ne dispense jamais d’effectuer la suivante. Un utilisateur qui possède un JWT valide n’est pas forcément autorisé à lire les données d’autrui. Un utilisateur autorisé à mettre à jour ses propres données n’est pas forcément autorisé à modifier son état de facturation.
Une fois cette décomposition par étapes acquise, la réponse à chaque question cesse d’être un exercice de mémorisation.
flowchart LR
accTitle: Différence entre authentification et autorisation
accDescr: L'authentification confirme le sujet, l'autorisation confirme les opérations que ce sujet est habilité à effectuer
auth[Authentification<br/>qui est-ce]
authz[Autorisation<br/>que peut-il faire]
auth --> authz
Figure 2 : différence entre authentification et autorisation. L’authentification vient en premier, l’autorisation est une vérification distincte.
4. Question 1 — Qu’est-ce que « sans état » ?
La question 1 interroge sur l’un des principes de conception d’une API RESTful : la caractéristique de ne pas gérer de session.
La réponse est sans état (stateless).
Être sans état signifie que chaque requête contient à elle seule toutes les informations nécessaires au traitement, sans que le serveur ait besoin de se souvenir de l’état de la conversation issu de la requête précédente. Dans ce problème, l’application mobile joint un JWT à l’en-tête Authorization de chaque requête. Le serveur valide ce JWT et identifie l’utilisateur de cette requête.
Une erreur d’interprétation fréquente consiste à lire « sans état » comme « le serveur ne conserve absolument aucun état ». En réalité, il conserve normalement les états suivants :
- La base de données stockant les informations d’utilisateur et les données de santé
- L’état de facturation
- La valeur du code d’authentification, sa date d’expiration, le nombre d’échecs
- La clé de signature du JWT
- Pour une conception utilisant une liste de révocation, ces informations de révocation
- Les journaux et les enregistrements d’audit
Ce que le serveur ne conserve pas, c’est un état de session côté serveur destiné uniquement à faire perdurer une conversation, qui servirait de prérequis à chaque appel d’API.
De plus, être sans état n’améliore pas automatiquement la sécurité. Envoyer le JWT à chaque fois facilite la répartition horizontale de charge, mais si la validation du JWT est erronée, cette erreur se propage uniformément à tous les nœuds. La propriété architecturale et la justesse sécuritaire sont deux choses distinctes.
5. Question 2(1) — Le code à 4 chiffres tombe en moyenne au bout de 500 secondes
L’API d’authentification envoie par e-mail un nombre à 4 chiffres si l’identifiant et le mot de passe correspondent. Ensuite, si l’identifiant d’utilisateur et le code à 4 chiffres correspondent, un JWT est émis. Le code est valide pendant 10 minutes à partir de sa génération.
Lors de l’audit, 10 tentatives par seconde étaient possibles. Il faut déterminer, en moyenne, en combien de temps on parvient à le franchir.
Le calcul, c’est « la moitié du nombre de candidats »
Un nombre à 4 chiffres, en incluant les zéros de tête, donne les 10 000 possibilités suivantes.
0000, 0001, 0002, ... , 9999
Si la bonne réponse est choisie uniformément, le nombre moyen de tentatives qu’un attaquant essayant dans l’ordre, sans répétition, doit effectuer pour l’atteindre est la moitié du nombre de candidats.
Nombre moyen de tentatives = 10 000 ÷ 2 = 5 000
Temps moyen = 5 000 ÷ 10 tentatives/s = 500 secondes
Le blanc b est donc 500.
Dans le pire des cas, cela prend 1 000 secondes, mais la question porte sur la moyenne. Et comme la durée de validité du code est de 600 secondes, elle est plus longue que le temps moyen de 500 secondes nécessaire pour le franchir. C’est la raison pour laquelle on juge le risque de franchissement élevé.
flowchart LR
accTitle: Perception du temps pour le code d'authentification à 4 chiffres
accDescr: Essayer 10 000 possibilités à 10 tentatives par seconde réussit en moyenne au bout de 5 000 tentatives, soit 500 secondes, ce qui est plus court que la durée de validité de 600 secondes
A[10 000 candidats possibles] -->|Moyenne 10 000 / 2 = 5 000 tentatives| B[Temps moyen de franchissement : 500 secondes]
C[Durée de validité : 600 secondes] -->|500 s < 600 s| D[Franchissable dans la durée de validité]
Figure 9 : perception du temps pour le code d’authentification à 4 chiffres. Essayer en moyenne la moitié du nombre de candidats permet de réussir dans la durée de validité.
Raccourcir seulement la durée d’expiration ne suffit pas si les candidats sont peu nombreux
La robustesse d’un code d’authentification ne se détermine ni par le nombre de chiffres seul, ni par la durée de validité seule.
Nombre de tentatives possibles pendant la durée de validité
= tentatives par seconde × durée de validité
= 10 × 600
= 6 000
En essayant dans l’ordre des valeurs non répétées, on peut vérifier 60 % des 10 000 possibilités dans la durée de validité. Même avec une durée d’expiration en place, cela ne suffit pas si le nombre de tentatives n’est pas limité.
La norme actuelle NIST SP 800-63B exige au moins 6 chiffres pour un secret de courte durée utilisé en authentification hors bande, et impose une limitation du nombre de tentatives en dessous de 64 bits. Elle demande également de ne pas utiliser le courrier électronique pour l’authentification hors bande4. L’examen répond dans le cadre de la spécification donnée (4 chiffres, envoi par e-mail), mais pour une conception nouvelle en pratique, il faut aussi remettre en question ce postulat lui-même.
6. Question 2(2) — alg=none : le problème d’avoir « laissé l’attaquant choisir la méthode de vérification »
Le JWT de ce problème se compose de trois parties : l’en-tête, la charge utile et la signature.
base64url(header).base64url(payload).base64url(signature)
L’en-tête indiquait RS256 comme algorithme de signature. La charge utile contenait l’identifiant d’utilisateur, l’heure d’émission et la date d’expiration.
L’auditeur a modifié les deux éléments suivants :
- Changer
algdans l’en-tête, deRS256àNONE - Changer l’identifiant d’utilisateur dans la charge utile pour un autre utilisateur
En envoyant ce JWT, la validation réussissait et permettait d’usurper l’identité d’autrui.
flowchart LR
accTitle: Déroulement de l'attaque JWT alg=none
accDescr: À partir d'un JWT valide, changer alg en none et réécrire l'identifiant d'utilisateur pour le faire accepter
A[JWT valide<br/>alg=RS256<br/>user=user01] -->|Change alg dans l'en-tête vers none| B[JWT falsifié<br/>alg=none<br/>user=user02]
B -->|Contourne la validation de signature| C[Le serveur accepte<br/>l'identité user02]
Figure 3 : déroulement de l’attaque JWT alg=none. C’est l’attaquant qui choisit l’algorithme de vérification.
none n’est pas une faute de frappe
La RFC 7519 définit un « Unsecured JWT », c’est-à-dire un JWT dont alg vaut none, sans signature ni chiffrement5. La valeur none n’est donc pas totalement absente de la spécification.
Le problème, c’est que l’API, qui aurait dû n’accepter que des JWT signés, a accepté le none spécifié par l’attaquant.
Écrit de façon conceptuelle, le traitement vulnérable ressemble à ceci :
1. Lire l'en-tête du JWT
2. Regarder l'alg indiqué dans l'en-tête, et choisir la méthode de vérification
3. Si alg vaut none, ne pas effectuer la vérification de signature
4. Faire confiance à l'identifiant d'utilisateur de la charge utile
C’est une entrée contrôlée par l’attaquant qui décide de la robustesse même de la sécurité.
La réponse attendue à l’examen
La question demande, pour la bibliothèque Q corrigée, « sur quelles données » et « quelle vérification » elle effectue, chacune en 20 caractères maximum.
L’exemple de réponse est le suivant.
| Élément | Point clé de la réponse |
|---|---|
| Données à vérifier | La valeur indiquée dans alg de l’en-tête du JWT |
| Contenu de la vérification | Vérifier qu’elle n’est pas NONE |
En tant que correction directe de la vulnérabilité décrite dans l’énoncé, c’est la bonne réponse.
En pratique, ne pas se contenter de « tout sauf NONE »
Il faut ici distinguer la réponse attendue à l’examen de la recommandation en pratique.
La RFC 8725 exige que la bibliothèque JWT fasse spécifier par l’appelant un ensemble d’algorithmes autorisés, et n’utilise jamais rien en dehors de cet ensemble6. Autrement dit :
Mauvaise approche :
accepter si token.header.alg != "none"
Bonne approche :
n'accepter que si la valeur figure dans serverConfig.allowedAlgorithms
exemple : allowedAlgorithms = ["RS256"]
Rejeter seulement none peut laisser subsister un autre algorithme faible, ou une confusion d’algorithme entre un schéma à clé publique et un schéma à clé symétrique. Le principe n’est pas d’accumuler des conditions négatives pour ce qu’on accepte, mais de fixer étroitement les conditions de ce qu’on autorise.
Pour la validation d’un JWT, il faut vérifier, selon l’usage, au moins les points suivants, pas seulement l’algorithme.
| Élément | À vérifier |
|---|---|
| Signature | Peut-elle être validée avec la clé et l’algorithme attendus ? |
iss |
S’agit-il d’un émetteur de confiance ? |
aud |
Le jeton a-t-il été émis pour cette API ? |
exp |
Sommes-nous dans la période de validité ? |
nbf |
Ne sommes-nous pas avant l’heure de début d’utilisation ? |
sub ou identifiant d’utilisateur |
S’agit-il d’un sujet valide dans l’application ? |
| Type de jeton | N’y a-t-il pas de confusion entre jeton d’identité, jeton d’accès, etc. ? |
Dans ce problème, la clé de la charge utile s’appelle user, mais en pratique, il vaut mieux utiliser le sub standard, ou définir clairement le sens d’une revendication (claim) personnalisée.
flowchart TB
accTitle: Validation JWT sûre et validation JWT dangereuse
accDescr: La validation dangereuse dépend de alg, la validation sûre utilise une liste d'autorisation côté serveur
subgraph "Validation dangereuse"
D1[Lit alg dans l'en-tête du JWT]
D2[Accepte si alg vaut none]
D1 --> D2
end
subgraph "Validation sûre"
S1[Algorithme autorisé en configuration serveur<br/>ex. RS256]
S2[Vérifie que alg de l'en-tête du JWT<br/>figure dans la liste d'autorisation]
S3[Valide signature, iss, aud, exp]
S1 --> S2 --> S3
end
Figure 4 : validation sûre et validation dangereuse. En pratique, on fixe étroitement les algorithmes autorisés.
Le Base64url n’est pas du chiffrement
Il existe un autre malentendu fréquent au sujet des JWT. L’en-tête et la charge utile sont représentés en base64url, mais ce n’est pas du chiffrement. N’importe qui peut les décoder et les lire.
Ce que garantit la signature, c’est que, lorsqu’elle peut être validée correctement, le contenu n’a pas été falsifié depuis son émission. Cela ne signifie pas qu’il est permis de placer des informations personnelles que l’on veut garder secrètes dans la charge utile d’un JWT signé.
7. Question 2(3) — Même avec un JWT valide, changer mid permettait de lire les données d’autrui
Voici maintenant une attaque qui ne falsifie pas le JWT lui-même.
L’API utilisateur reçoit, en GET ou en PUT, un identifiant d’utilisateur nommé mid. Le module commun P récupère et met à jour, depuis la base de données, les informations d’utilisateur liées à ce mid.
La structure de l’attaque est simple.
Identifiant d'utilisateur du JWT : user01 ← JWT correctement signé
mid de la requête : user02 ← modifié par l'attaquant
Comme la signature du JWT est correcte, l’authentification réussit. Mais l’API fait confiance à mid=user02 tel quel, et renvoie les informations de user02.
C’est un exemple typique de ce que l’OWASP API Security Top 10 2023 appelle Broken Object Level Authorization (BOLA). Lorsqu’on accède à des données à l’aide d’un identifiant d’objet spécifié par l’utilisateur, il faut vérifier l’autorisation pour cet objet à chaque fois7.
flowchart LR
accTitle: Attaque BOLA
accDescr: Utilise un JWT valide tout en changeant le mid de la requête vers un autre identifiant d'utilisateur
A[Attaquant] -->|JWT : user01<br/>mid : user02| B[API utilisateur]
B -->|Fait confiance à mid| C[Renvoie les informations de user02 depuis la BDD]
Figure 5 : attaque BOLA. L’authentification passe, mais l’autorisation n’est pas vérifiée.
La réponse attendue
Le trait souligné ② du tableau 5 demande, en 40 caractères maximum, le traitement à ajouter au processus d’appel du module commun P.
L’exemple de réponse est le suivant.
Un traitement qui vérifie si l’identifiant d’utilisateur contenu dans le JWT correspond à la valeur de mid
L’avantage de vérifier cela dans le module commun P est de pouvoir appliquer facilement la même autorisation à la fois à GET et à PUT, et à toute autre API qui utiliserait P à l’avenir. Si l’on copie le même traitement de comparaison dans chaque écran ou chaque point de terminaison, il finira par manquer quelque part.
Une conception plus sûre consiste à ne pas recevoir mid
Pour une API qui ne fait que récupérer et mettre à jour les informations de l’appelant lui-même, il n’est pas nécessaire de recevoir l’identifiant d’utilisateur depuis le client.
GET /users/me
Authorization: Bearer <JWT>
Côté serveur, on extrait le sujet du JWT validé.
principal = validateJwt(request.authorization)
userId = principal.subject
return repository.getUser(userId)
La mise à jour se fait de la même façon.
principal = validateJwt(request.authorization)
input = validateProfileUpdate(request.body)
repository.updateProfile(
userId = principal.subject,
name = input.name,
age = input.age
)
Un traitement de comparaison protège, à condition d’être écrit. Mais une conception qui ne reçoit jamais d’identifiant cible depuis l’extérieur réduit le risque même d’oublier d’écrire ce traitement de comparaison.
Si un administrateur a besoin d’agir sur les informations d’un autre utilisateur, il faut séparer les points de terminaison ainsi :
PUT /users/me pour l'utilisateur ordinaire
PUT /admin/users/{userId} pour l'administrateur
Le point de terminaison administrateur exige des permissions distinctes, un journal d’audit, et si nécessaire une réauthentification. Cela rend la frontière de la politique d’autorisation plus visible que d’« ajouter une exception à l’API utilisateur ordinaire uniquement pour le cas administrateur ».
flowchart LR
accTitle: Comment se prémunir de BOLA
accDescr: Ne pas utiliser le mid de la requête ; déterminer ou vérifier la cible à partir du sujet du JWT
A[Utilisateur] -->|GET /users/me + JWT| B[API]
B -->|Récupère le sub du JWT| C{Si mid fourni,<br/>correspond-il au sub ?}
C -->|Correspond| D[Renvoie ses propres données]
C -->|Ne correspond pas| E[Refus 403]
B -->|Pas de mid| F[Recherche en BDD via le sub du JWT]
Figure 6 : comment se prémunir de BOLA. Ne pas recevoir mid, ou le comparer au sujet du JWT.
Distinguer authentification et autorisation en une phrase
À l’examen comme en pratique, la formulation suivante est utile.
- Authentification : qui est-ce ?
- Autorisation : que cette personne est-elle autorisée à faire ?
Réussir la validation de la signature du JWT ne signifie que « le sujet représenté par ce jeton mérite confiance ». Que « ce sujet soit autorisé à lire user02 » doit être vérifié séparément.
8. Question 2(4) — status=paid est un défaut d’autorisation au niveau de la propriété
La spécification de l’API utilisateur définit les paramètres de mise à jour suivants.
mid identifiant d'utilisateur
name nom
age âge
Or l’auditeur a ajouté la valeur suivante, absente de la spécification.
status=paid
Le statut d’un utilisateur gratuit est alors devenu celui d’un utilisateur payant.
Selon l’énoncé, le service L ne validait pas les paramètres reçus et les transmettait tous au module commun P. P était conçu pour mettre à jour directement la base de données.
La réponse au blanc c est le module commun P.
flowchart TB
accTitle: Mass Assignment
accDescr: Un status=paid hors spécification est ajouté et se répercute intégralement sur l'objet interne
A[Spécification de l'API<br/>mid / name / age] -->|L'attaquant ajoute status=paid| B[Corps de la requête]
B -->|Liaison automatique| C[Module commun P]
C -->|Enregistrement en BDD| D[L'état de facturation devient paid]
Figure 7 : Mass Assignment. Une propriété hors spécification se répercute intégralement sur l’objet interne.
La différence avec BOLA
La substitution de mid au chapitre précédent et l’ajout de status cette fois-ci se ressemblent, mais la granularité protégée diffère.
| Vulnérabilité | Ce que l’attaquant modifie | Ce qu’il fallait vérifier |
|---|---|---|
Substitution de mid |
L’objet cible | Cet utilisateur est-il autorisé à accéder à cet enregistrement d’utilisateur ? |
Ajout de status |
Une propriété au sein de l’objet | Cet utilisateur est-il autorisé à modifier ce champ ? |
Dans l’OWASP API Security Top 10 2023, ce second cas est traité sous le nom de Broken Object Property Level Authorization, qui inclut désormais l’ancien Mass Assignment dans cette catégorie8.
« Injecter le JSON directement dans l’entité » est dangereux
L’implémentation vulnérable ressemble, conceptuellement, à ceci :
entity = repository.find(body.mid)
bindAllProperties(entity, body)
repository.save(entity)
Même si l’écran ne comporte de champs de saisie que pour name et age, un attaquant peut construire directement une requête HTTP. Ce qui n’apparaît pas dans l’interface utilisateur n’est pas une frontière de sécurité.
Une implémentation sûre explicite les champs modifiables.
input = parseExactSchema(body, fields = ["name", "age"])
entity = repository.find(authenticatedUserId)
entity.name = input.name
entity.age = input.age
repository.save(entity)
Deux points sont importants ici :
- Ne placer, dans le type de saisie de mise à jour, que les champs que l’utilisateur est autorisé à modifier
- Ne pas ignorer silencieusement un champ inconnu hors spécification, mais le rejeter comme une erreur si possible
Ignorer silencieusement un champ inconnu permet de dissimuler l’échec d’une attaque, mais aussi de passer à côté d’erreurs d’implémentation du client ou de signes d’une attaque. En l’absence de contrainte de compatibilité, rejeter avec un schéma strict facilite les investigations ultérieures.
flowchart TB
accTitle: Autorisation au niveau du champ
accDescr: Le DTO de mise à jour ne contient qu'une liste d'autorisation, et rejette les propriétés inconnues
subgraph "DTO de mise à jour (liste d'autorisation)"
D1["name"]
D2["age"]
end
A[Corps de la requête] -->|Validation du schéma| B{Uniquement des<br/>champs autorisés ?}
B -->|Oui| C[Met à jour name/age de l'entité]
B -->|Non| D[Renvoie une erreur]
E[Service de paiement<br/>notification vérifiée] -->|Voie dédiée| F[Met à jour status=paid]
Figure 8 : autorisation au niveau du champ. Les champs modifiables sont limités par une liste d’autorisation, et l’état de facturation est modifié par une voie distincte.
status ne doit changer qu’à partir du résultat du paiement
status=paid ne fait pas partie du profil de l’utilisateur. C’est un état qui découle d’un fait constaté côté serveur : la réussite du paiement.
Mise à jour du profil de l'utilisateur
-> seuls name / age sont modifiables
Notification vérifiée du service de paiement
-> vérifie paymentId
-> empêche un traitement en double
-> change status vers paid
Même stockées dans la même colonne de base de données, le droit de modifier et la voie pour le faire sont deux choses distinctes. Si l’on utilise directement l’entité interne comme type de saisie pour une API externe, cette frontière disparaît.
9. Question 2(5) — La mesure contre la force brute consiste à conserver le nombre d’échecs comme un état
Contre la force brute sur le code à 4 chiffres, il faut répondre, en 30 caractères maximum, au traitement qui va dans le blanc d du tableau 5. Le seuil est fixé à 10.
L’exemple de réponse est le suivant.
Un traitement qui verrouille le compte lorsque le nombre d’échecs consécutifs dépasse le seuil
Ce point ne contredit pas le caractère sans état de la question 1. Ne pas conserver l’état de la conversation d’un appel d’API comme une session serveur, et rendre persistant le nombre d’échecs nécessaire à une décision de sécurité, sont deux choses distinctes.
flowchart LR
accTitle: Présence ou absence de limitation du nombre de tentatives
accDescr: Sans limite, l'attaque réussit en moyenne en 500 secondes ; avec une limite du nombre d'échecs, la vitesse d'attaque peut être freinée
subgraph "Sans limite"
A1[10 tentatives par seconde] -->|environ 500 secondes| B1[Authentification réussie]
end
subgraph "Avec limite"
A2[Verrouillage après 10 échecs] -->|Chute brutale de la vitesse d'attaque| B2[Compte verrouillé]
C2[Délai progressif] --> B2
end
Figure 10 : présence ou absence de limitation du nombre de tentatives. La limitation du nombre d’échecs permet d’arrêter la force brute de façon pratique.
En pratique, ne pas se limiter à « le verrouillage permanent, et rien d’autre »
La limitation par compte est nécessaire, mais si l’attaquant connaît l’identifiant d’utilisateur d’une autre personne, il peut délibérément provoquer 10 échecs pour exclure l’utilisateur légitime. C’est pourquoi, en pratique, on combine les éléments suivants.
| Contrôle | Rôle |
|---|---|
| Nombre d’échecs par compte | Arrête la force brute sur un seul compte |
| Délai d’attente progressif | Tolère les erreurs de saisie de l’utilisateur légitime tout en ralentissant la vitesse d’attaque |
| Contrôle par adresse IP source, appareil, ASN, etc. | Freine une attaque qui tente peu de fois sur un grand nombre de comptes |
| Évaluation basée sur le risque | Restreint fortement une région, un appareil ou une vitesse inhabituels |
| Notification à l’utilisateur | Permet de remarquer une attaque ou une erreur de manipulation |
| Procédure de récupération sûre | Empêche que le guichet de déverrouillage devienne lui-même une voie d’attaque |
De plus, il ne faut jamais remettre le nombre d’échecs à zéro lors du renvoi d’un code. Sinon, l’attaquant peut faire renaître son quota de tentatives à chaque appel de l’API de renvoi. La norme actuelle NIST SP 800-63B exige elle aussi de ne pas réinitialiser le nombre d’échecs même en générant un nouveau secret d’authentification4.
Faire en sorte que le code d’authentification ne soit utilisable qu’une seule fois
L’énoncé se concentre sur la durée de validité, mais en pratique, il faut aussi :
- Invalider immédiatement un code ayant réussi
- Refuser la réutilisation d’un même code
- Ne jamais conserver le code lui-même dans les journaux
- Répondre de façon à ce que le succès ou l’échec de la comparaison du code ne permette pas de déduire l’existence de l’utilisateur
- Limiter également le nombre d’appels de l’API d’envoi du code
Dès lors qu’on utilise un secret court, on ne peut pas confier toute la sécurité à la seule génération aléatoire.
flowchart TB
accTitle: Mesures pour le code d'authentification
accDescr: Se protéger en combinant nombre de chiffres et durée d'expiration avec la limitation des tentatives, le refus de réutilisation, les notifications, etc.
A[Code d'authentification] --> B[Augmenter le nombre de chiffres]
A --> C[Raccourcir la durée de validité]
A --> D[Limiter le nombre de tentatives]
A --> E[Invalider après succès]
A --> F[Ne pas réinitialiser le nombre d'échecs au renvoi]
A --> G[Ne pas conserver le code dans les journaux]
A --> H[Contrôle par source]
Figure 11 : mesures pour le code d’authentification. Au-delà du nombre de chiffres et de la durée d’expiration, combiner contrôle des tentatives et exploitation.
10. Distinguer d’un seul coup d’œil les quatre points de la question 2
Voici, organisés par la valeur que l’attaquant contrôlait, les points souvent confondus dans la question 2.
| Attaque | Valeur modifiée par l’attaquant | Ce qu’il ne fallait pas croire | Mesure de fond |
|---|---|---|---|
| Falsification du JWT | alg de l’en-tête du JWT, identifiant d’utilisateur de la charge utile |
L’algorithme de vérification déclaré par le jeton lui-même | Fixer côté serveur les algorithmes autorisés |
| Récupération des informations d’autrui | mid de la requête |
L’identifiant cible spécifié par le client | Comparer avec le sujet du JWT, ou déterminer l’identifiant cible à partir du JWT |
| Passage à utilisateur payant | status hors spécification |
Toutes les propriétés liées automatiquement | Établir une liste d’autorisation pour les propriétés modifiables |
| Franchissement du code à 4 chiffres | Le candidat otp |
Des tentatives d’authentification illimitées | Introduire une limitation du nombre d’échecs, un délai, une évaluation du risque |
Il est important de ne pas tout regrouper sous « valider les valeurs saisies ».
algrelève d’une politique de traitement cryptographiquemidrelève de l’autorisation au niveau de l’objetstatusrelève de l’autorisation au niveau de la propriétéotprelève de la résistance à la devinette en ligne
Même à l’intérieur d’une seule et même requête HTTP, la raison de les protéger diffère.
11. Question 3(1) — Confirmer l’exécution de code externe sans rien détruire
Après le lancement du service, une vulnérabilité critique V est publiée dans la bibliothèque open source H, largement utilisée. Le déroulement décrit dans l’énoncé est le suivant.
- L’attaquant place une chaîne contenant un JNDI Lookup dans un en-tête HTTP et l’envoie
- Le serveur ciblé écrit cette valeur dans les journaux
- La bibliothèque vulnérable évalue le JNDI Lookup et interroge un serveur LDAP contrôlé par l’attaquant
- La réponse LDAP renvoie l’URL d’un serveur HTTP contrôlé par l’attaquant
- Le serveur ciblé récupère un fichier de classe et exécute une commande
On peut lire cela comme une attaque de type Log4Shell (CVE-2021-44228), dont le nom propre est tu. La documentation d’Apache décrit elle aussi cette vulnérabilité comme permettant, dès lors qu’un attaquant contrôle un message de journal ou un paramètre, l’exécution d’un code arbitraire chargé depuis un serveur LDAP9.
flowchart LR
accTitle: Flux de vérification d'une vulnérabilité de type Log4Shell
accDescr: Vérifie, par un rappel inoffensif, si la chaîne allant du JNDI jusqu'à l'exécution de code externe fonctionne
A[Attaquant] -->|Injecte jndi:ldap://...<br/>dans x-api-version| B[Serveur vulnérable]
B --> C[Traitement du journal]
C -->|JNDI Lookup| D[Serveur LDAP malveillant]
D -->|Réponse URL HTTP| E[Serveur HTTP malveillant<br/>index.html]
E -->|Enregistre le GET| F[Journal d'accès<br/>du serveur de test]
F -->|Confirmation d'atteinte| G[Vulnérabilité confirmée]
Figure 12 : flux de vérification d’une vulnérabilité de type Log4Shell. On confirme l’atteinte par l’enregistrement d’un accès HTTP, et non par une commande destructrice.
Le code de vérification ne provoque qu’un accès HTTP inoffensif
L’entreprise G exécute un code de vérification sans impact sur le système, pour confirmer si la vulnérabilité V peut être exploitée depuis l’extérieur. La seule commande que ce code de vérification exécute est de récupérer index.html sur un serveur de test.
La question 3(1) demande ce qu’il faut implémenter sur le serveur de test pour confirmer que la commande a été exécutée.
L’exemple de réponse est le suivant.
Un mécanisme qui enregistre et permet de confirmer l’accès à index.html du serveur de test
Si le journal d’accès du serveur web conserve une requête GET provenant du serveur ciblé, cela confirme au moins que la chaîne suivante a fonctionné.
Requête HTTP externe
-> traitement du journal
-> JNDI Lookup
-> réponse LDAP
-> récupération de classe
-> exécution de la commande de vérification
-> accès HTTP vers le serveur de test
Pourquoi « afficher du texte à l’écran » ne suffit-il pas ?
La cible de l’attaque est le serveur. Rien ne garantit qu’un changement apparaisse à l’écran du navigateur de l’utilisateur. De plus, même si la vulnérabilité existe, la communication sortante intermédiaire peut être bloquée par un pare-feu.
Enregistrer l’accès du côté du serveur de test constitue une preuve observable que le serveur ciblé a bien atteint l’extérieur.
Lorsqu’on effectue une vérification du même type en pratique, il faut impérativement respecter les points suivants.
- Obtenir l’accord explicite du propriétaire du système cible
- Choisir une méthode de vérification sans impact sur la production, ou dont l’impact est acceptable
- Ne jamais utiliser de commande destructrice (écriture, suppression, changement de configuration, etc.)
- Gérer soi-même le domaine et le serveur de vérification
- Consigner l’heure de vérification, la source, la cible, et le rappel attendu
- Retirer, après la vérification, les serveurs LDAP/HTTP temporaires et les identifiants utilisés
« Confirmer l’exécution de code arbitraire » et « exécuter un code arbitraire dangereux » ne sont pas la même chose. Il faut viser l’effet de bord minimal qui atteint l’objectif.
12. Questions 3(2) et 3(3) — Le WAF inspecte les en-têtes HTTP
Le WAF du service N permet de choisir GET, POST, PUT, ANY, Header, COOKIE ou Multipart comme cible d’inspection.
Le code d’attaque se trouve dans la valeur de l’en-tête HTTP nommé x-api-version. Les blancs e et f du tableau 6 valent donc tous les deux Header.
Faire correspondre directement l’emplacement décrit dans l’énoncé à la cible d’inspection du WAF
Il s’agit ici moins de connaissances générales que de lire le flux de données de l’énoncé.
Emplacement de la chaîne d'attaque :
en-tête x-api-version
|
v
Cible d'inspection du WAF :
Header
Ce n’est ni un paramètre GET ni un corps POST. Plutôt que de regarder la liste des fonctionnalités du WAF et de choisir « ANY parce que ça ressemble à une attaque », il faut répondre en fonction de l’emplacement où l’attaquant a placé la valeur dans l’énoncé.
Gérer l’inversion des majuscules et des minuscules
La première version, conceptuellement, suivait les règles suivantes.
Header \Wjndi\W bloquer
Header \Wldap\W bloquer
Mais en inversant la casse, comme dans jNdI, on peut contourner un motif ne comportant que des minuscules.
L’exemple de réponse à la question 3(3) est l’une des expressions suivantes.
\W[jJ][nN][dD][iI]\W
\W(j|J)(n|N)(d|D)(i|I)\W
Dans le fascicule d’examen, la barre oblique inverse peut apparaître sous la forme d’un signe yen dans le rendu de caractères de l’environnement japonais, mais il s’agit bien de \W en tant qu’expression régulière. \W correspond à tout caractère autre qu’alphanumérique ou trait de soulignement. Dans la syntaxe du JNDI Lookup, des caractères non alphanumériques comme ${ ou : apparaissent avant et après jndi, d’où leur inclusion dans le motif.
Selon la même logique, on peut aussi rendre ldap insensible à la casse.
\W[lL][dD][aA][pP]\W
Ne pas considérer cette expression régulière comme une « protection complète contre Log4Shell »
L’examen demande de répondre par l’expression régulière correspondant à la méthode de contournement indiquée dans l’énoncé. Dans une attaque réelle, il peut exister des variantes difficiles à couvrir par une simple signature : découpage de la chaîne, autre type de Lookup, encodage, autre protocole, etc.
En pratique, ce dispositif se positionne donc ainsi :
- Bloquer provisoirement, via le WAF, les motifs d’attaque déjà identifiés
- Vérifier si la bibliothèque concernée est réellement présente
- Restreindre les communications sortantes LDAP, RMI, ou HTTP inutiles
- Mettre à jour vers la version corrigée
- Continuer à vérifier les journaux après la mise à jour pour rechercher toute compromission
Le WAF est une couche qui fait gagner du temps jusqu’à la sortie de la version corrigée.
flowchart TB
accTitle: Positionnement du WAF
accDescr: Le WAF est une couche d'atténuation provisoire ; la mesure de fond est la mise à jour de la bibliothèque vers une version corrigée
A[Publication d'une vulnérabilité critique] --> B[Vérification de l'impact]
B --> C[Atténuation provisoire]
C -->|Règle WAF<br/>détection/blocage| D[Bloque temporairement le motif d'attaque]
C -->|Restriction des communications sortantes| E[Ferme la voie d'exploitation]
D --> F[Mise à jour vers la bibliothèque corrigée]
E --> F
F --> G[Vérification a posteriori et prévention de la récidive]
Figure 13 : positionnement du WAF. Le WAF fait gagner du temps jusqu’à la sortie de la version corrigée ; la mesure de fond est la mise à jour.
13. Question 3(4) — Pourquoi commencer par le mode « détection »
Au sujet de la règle WAF modifiée, M. Z, spécialiste agréé en sécurité de l’information (RISS), conseille de régler le comportement sur « détection » plutôt que sur « blocage » pendant une certaine période après le lancement en production.
La question demande, chacun en 25 caractères maximum, l’avantage du mode détection et ce qu’il faut faire pour minimiser les dégâts.
L’exemple de réponse est le suivant.
| Élément | Point clé de la réponse |
|---|---|
| Avantage | Peut éviter un blocage causé par un faux positif |
| Ce qu’il faut faire | Examiner, à réception d’une alerte, s’il s’agit d’une attaque |
Le mode détection n’est pas un « mode où l’on ne fait rien »
En mode détection, le trafic correspondant aux règles est laissé passer tout en étant enregistré dans les journaux et en déclenchant des alertes. Même si les chaînes jndi ou ldap apparaissent par hasard dans un appel d’API normal, l’activité n’est pas interrompue immédiatement.
En contrepartie, l’exploitation doit prévoir ce qui suit.
Réception de l'alerte
|
v
Examen de la requête concernée
|
+-- trafic normal -> restreindre la règle, envisager une exception
|
+-- attaque -> isoler la cible, préserver les journaux, enquêter sur l'impact, passer au blocage
Si personne ne consulte jamais les alertes, le mode détection n’a aucun effet protecteur. La détection va toujours de pair avec une exploitation qui observe et décide.
Passer de la détection au blocage
La procédure de déploiement habituelle est la suivante.
- Appliquer le mode détection au trafic réel
- Classer les faux positifs et les vrais positifs
- Ajuster les en-têtes ciblés, les chemins, les API, les limites de caractères, etc.
- Confirmer que l’impact sur le trafic normal est acceptable
- Passer en mode blocage
- Surveiller le nombre de blocages et l’impact sur l’activité
Cela dit, ce sont les principes valables en temps normal. Si la vulnérabilité est critique, effectivement exploitée, et sans solution de contournement, on peut juger que les dégâts d’une compromission dépassent ceux d’un arrêt causé par un faux positif, et bloquer d’emblée. Dans la situation de l’examen, on choisit d’abord la détection afin de vérifier si le service reste utilisable comme avant.
flowchart LR
accTitle: Du mode détection au mode blocage du WAF
accDescr: Observer les alertes en mode détection, ajuster les faux positifs, puis passer en mode blocage
A[Mode détection] -->|Trafic réel| B[Génération d'alertes]
B --> C{Attaque ou<br/>faux positif ?}
C -->|Faux positif| D[Ajuster la règle]
D --> A
C -->|Attaque| E[Passer en mode blocage]
E --> F[Surveiller le nombre de blocages et l'impact sur l'activité]
Figure 14 : du mode détection au mode blocage. On observe et on ajuste d’abord, puis on passe au blocage une fois l’impact confirmé acceptable.
14. Le WAF est une mesure provisoire ; la mise à jour est la mesure de fond
Dans l’énoncé, le site officiel de la bibliothèque H ne disposait encore ni de version corrigée ni de mesure provisoire, et les règles WAF exhaustives du fournisseur cloud pouvaient prendre jusqu’à 72 heures. C’est pourquoi l’entreprise G vérifie elle-même l’impact et bloque provisoirement, au moins, les motifs déjà identifiés.
Cet ordre est la forme de base de la réponse à incident.
| Étape | Objectif | Réponse dans ce problème |
|---|---|---|
| Vérification de l’impact | Déterminer si l’on est réellement en danger | Confirmer, par un rappel inoffensif, la possibilité d’exploitation externe |
| Atténuation provisoire | Gagner du temps jusqu’à la correction | Règle WAF, détection/blocage, restriction des communications sortantes |
| Correction de fond | Éliminer la cause vulnérable | Mise à jour vers la bibliothèque corrigée |
| Vérification a posteriori | Rechercher une exploitation déjà survenue | Examen des journaux WAF, application, DNS, proxy, etc. |
| Prévention de la récidive | Accélérer la prochaine décision | Inventaire des dépendances, SBOM, procédure de mise à jour, canal de communication |
« Ne pas savoir si on l’utilise » constitue le plus grand facteur de retard
D’après l’énoncé, même lorsque l’entreprise G interroge l’entreprise F pour savoir si elle utilise la bibliothèque H, la réponse prend du temps car elle nécessite une analyse détaillée de la configuration.
En pratique, si l’on commence à chercher des fichiers JAR seulement après la publication d’une vulnérabilité critique, la réponse prend du retard. Il faut disposer, dès le temps normal, au moins des éléments suivants :
- La liste des dépendances directes et transitives
- Les composants et versions réellement inclus dans les livrables
- Les services, conteneurs et appareils sur lesquels ils sont déployés
- La procédure pour mettre à jour une bibliothèque dépendante, reconstruire et redéployer
- Le canal de communication pour approuver un changement d’urgence
- Les destinations autorisées pour les communications sortantes, et l’impact si on les coupe
- L’emplacement et la méthode de recherche des journaux
Un SBOM n’est pas une fin en soi. C’est un index permettant de répondre rapidement à la question « quels systèmes en cours d’exécution cette vulnérabilité affecte-t-elle ? »
Ne pas clore l’enquête une fois la mise à jour effectuée
Il est possible qu’une attaque ait déjà eu lieu avant ou après la publication de la vulnérabilité. Mettre à jour vers la version corrigée arrête les exploitations futures, mais ne fait pas disparaître des identifiants déjà compromis ou une porte dérobée déjà installée.
Pour un type Log4Shell, il faut examiner au moins les points suivants.
- Requêtes HTTP contenant des chaînes suspectes évoquant JNDI ou LDAP
- Communications du serveur d’application vers un LDAP, un RMI ou un HTTP externe
- Démarrage de processus enfants inhabituels
- Création de fichiers JAR, class, scripts ou exécutables suspects
- Accès aux identifiants cloud ou aux variables d’environnement
- Modifications d’authentification, changements de permissions, envois externes avant et après la mise à jour
Il est important de ne pas conclure « pas d’attaque » sur la seule base des journaux du WAF. Il existe des voies internes qui ne passent pas par le WAF, ainsi que des journaux qui n’ont pas été conservés dans le passé.
15. Une méthode de lecture pour marquer des points plus facilement à l’examen
Ce problème est moins un exercice de connaissances qu’un exercice de lecture de l’écart entre la spécification et l’implémentation.
15.1 Séparer, dans les tableaux, la « spécification » et l’« implémentation »
Dans le problème de status, une valeur absente de la spécification de l’API passe malgré tout dans l’implémentation.
Spécification :
mid / name / age
Implémentation :
tous les paramètres reçus sont envoyés à P
En repérant cet écart, on comprend que le blanc c est le module commun P.
15.2 Tracer une ligne sous chaque valeur modifiée par l’attaquant
Voici les valeurs modifiées dans chaque attaque.
algdans l’en-tête du JWT- L’identifiant d’utilisateur dans la charge utile du JWT
- Le
midd’un paramètre d’API status, hors spécificationotpde l’API d’authentification- L’en-tête HTTP
x-api-version
Presque toutes les questions demandent essentiellement « où cette valeur aurait-elle dû être vérifiée ? »
15.3 Ramener la réponse au vocabulaire de l’énoncé
En pratique, on peut parler de « BOLA », de « Mass Assignment », de « rate limiting ». Mais ce que demande la question, c’est un traitement concret, formulé selon la structure de l’énoncé.
Mauvais exemple :
Effectuer correctement l’autorisation.
Bon exemple :
Vérifier que l’identifiant d’utilisateur contenu dans le JWT correspond à la valeur de mid.
Mauvais exemple :
Mettre en place une mesure contre la force brute.
Bon exemple :
Verrouiller le compte lorsque le nombre d’échecs consécutifs dépasse le seuil.
Connaître seulement le nom abstrait ne suffit pas à produire une réponse notable dans la limite de caractères imposée.
15.4 Le WAF : suivre « où la valeur a été insérée »
La cible d’inspection du WAF ne se déduit pas du type d’attaque, mais de l’emplacement où la chaîne d’attaque est stockée.
insérée dans l'en-tête x-api-version
↓
la cible d'inspection est Header
Si le taux de bonnes réponses à la question 3(1) était relativement faible dans le commentaire de notation, c’est aussi parce que beaucoup de réponses ne correspondaient pas au déroulement de l’attaque de la figure 6. Il suffit de réécrire la procédure d’attaque sous forme de flèches pour voir clairement ce qu’il faut observer.
16. Une checklist utilisable pour les revues d’API en pratique
Voici une checklist pour ramener ce problème dans une revue de conception ou de code réelle.
Validation du JWT
- Les algorithmes de signature autorisés sont fixés dans la configuration serveur
noneet les algorithmes non prévus sont rejetés- La signature,
iss,aud,exp,nbfsont vérifiés selon l’usage - Le jeton d’identité, le jeton d’accès et le jeton de rafraîchissement ne sont jamais confondus
- Une procédure de rotation des clés et de révocation existe
- Aucune information à garder secrète n’est placée dans la charge utile du JWT
Autorisation au niveau de l’objet
- Changer l’identifiant à l’intérieur de la requête ne permet pas d’atteindre les données d’autrui
- L’autorisation est appliquée pour la liste, le détail, la mise à jour, la suppression et le téléchargement
- L’autorisation est mise en œuvre non pas dans l’écran, mais dans une couche commune d’accès aux données
- Pour une API réservée à soi-même, on a envisagé de dériver l’identifiant cible du jeton
- Les opérations administrateur sont séparées, en API et en politique, de celles de l’utilisateur ordinaire
Autorisation au niveau de la propriété
- Le type de saisie externe est distinct de l’entité de base de données
- Les champs modifiables sont énumérés via une liste d’autorisation
- Les propriétés hors spécification sont rejetées ou auditées
- Les états tels que les permissions, la facturation, l’approbation, le propriétaire ne peuvent pas être modifiés par une saisie de l’utilisateur
- La réponse ne contient pas non plus de propriétés sensibles inutiles
Tentatives d’authentification
- Une limite du nombre d’échecs par compte existe
- Un délai progressif ou un contrôle par source existent
- Le nombre d’échecs n’est pas réinitialisé lors du renvoi d’un code
- Le code d’authentification n’est utilisable qu’une seule fois
- Le code d’authentification et le mot de passe ne sont jamais conservés dans les journaux
- La procédure de déverrouillage/récupération ne constitue pas une autre voie d’authentification plus faible
Vulnérabilité critique d’une bibliothèque dépendante
- Les services en cours d’exécution peuvent être associés à leurs versions de dépendances
- Une procédure permet de vérifier l’impact par une méthode inoffensive
- Des mesures provisoires (WAF, restriction des communications sortantes, etc.) peuvent être appliquées
- Une exploitation existe pour que les alertes de détection soient vérifiées par un responsable
- Un canal de publication d’urgence permet de passer à la version corrigée
- Les journaux sont examinés avant la mise à jour pour rechercher une exploitation possible
17. Ce que ce problème révèle sur les deux visages des composants communs
Ce problème met en scène deux composants communs : la bibliothèque de gestion des JWT, Q, et le module commun P.
Un composant commun présente de grands avantages.
- Corriger un seul endroit répercute la correction à toutes les API qui l’utilisent
- Évite de dupliquer l’implémentation de l’autorisation et de la validation dans chaque fonctionnalité
- Permet de concentrer les cibles de test
- Permet d’uniformiser le format des journaux et de l’audit
Mais, en contrepartie, une erreur s’y propage à tout l’ensemble.
- Si la bibliothèque Q accepte
alg=none, toutes les API utilisant le JWT deviennent dangereuses - Si le module commun P accepte n’importe quel
midoustatus, à la fois GET et PUT deviennent dangereux - Si la bibliothèque vulnérable H est utilisée à la base, plusieurs voies qui écrivent des en-têtes HTTP dans les journaux deviennent une surface d’attaque
Ce qu’il faut donc mutualiser n’est pas un simple accès aux données. Il faut mutualiser les invariants de sécurité, et valider rigoureusement ce composant commun de façon isolée.
Par exemple, on peut définir le contrat de P ainsi :
P.getOwnUser(authenticatedSubject)
P.updateOwnProfile(authenticatedSubject, ProfileUpdate{name, age})
Il est plus sûr de ne pas exposer directement aux appelants ordinaires une API de bas niveau comme celle-ci :
P.getUser(arbitraryMid)
P.updateUser(arbitraryMap)
Cette dernière n’est nécessaire que sur des voies limitées, comme les traitements administratifs. Répandre une liberté de bas niveau à toutes les API revient à concevoir un système qui suppose que chaque appelant l’utilisera correctement, à chaque fois.
18. Conclusion
La question 1 de l’après-midi, session printemps 2024 (Reiwa 6), est un problème qui fait lire les points de sécurité des API un par un, séparément.
Utiliser un JWT ne signifie pas une authentification sûre. Laisser l’attaquant choisir l’algorithme de signature permet de réécrire l’identifiant d’utilisateur.
Que la signature du JWT soit correcte ne signifie pas que l’autorisation est correcte. Faire confiance au mid de la requête permet à un utilisateur légitime d’accéder aux informations d’autrui.
Pouvoir mettre à jour son propre objet ne signifie pas être autorisé à modifier toutes ses propriétés. Lier automatiquement un état interne comme status permet de réécrire les permissions ou l’état de facturation.
Qu’un code d’authentification ait une date d’expiration ne signifie pas qu’il résiste à la force brute. Il faut calculer le nombre de candidats et la vitesse de tentative, et limiter le nombre d’échecs.
Introduire une règle dans le WAF ne signifie pas avoir corrigé la vulnérabilité. On gagne du temps par la détection et le blocage, on vérifie l’impact, et on finit par mettre à jour la bibliothèque.
flowchart TB
accTitle: Tableau de correspondance entre vulnérabilités et mesures
accDescr: Associe chaque vulnérabilité à la frontière de confiance concernée et à sa mesure
A[Falsification du JWT] -->|Validation du jeton| B[Fixer les algorithmes autorisés]
C[Substitution de mid] -->|Autorisation au niveau de l'objet| D[Comparer avec le sujet du JWT / se passer de mid]
E[status=paid] -->|Autorisation au niveau de la propriété| F[Établir une liste d'autorisation pour le DTO de mise à jour]
G[Force brute sur le code à 4 chiffres] -->|Contrôle des tentatives d'authentification| H[Limitation des échecs / délai]
I[Vulnérabilité de type Log4Shell] -->|Entrée vers exécution| J[Mise à jour de la bibliothèque / WAF]
Figure 15 : tableau de correspondance entre vulnérabilités et mesures. Une mesure distincte pour chaque frontière franchie.
Un seul principe traverse tout ce problème.
Réussir la vérification précédente ne dispense jamais d’omettre la frontière de confiance suivante.
Dans les articles précédents de la série, nous avons expliqué le XSS stocké de la question 1 de l’automne 2023 (Reiwa 5) et l’exfiltration d’informations par le Wi-Fi invité de la question 2 de l’automne 2023 (Reiwa 5). Pour les points de contrôle de l’ensemble d’un site web, voir aussi Utiliser le guide « Comment concevoir un site web sûr » de l’IPA comme checklist.
flowchart TB
accTitle: Conclusion finale
accDescr: Montre que réussir la vérification précédente ne dispense jamais d'omettre la frontière de confiance suivante
A[Authentification réussie] --> B[Validation de la signature du JWT]
B --> C[Autorisation au niveau de l'objet]
C --> D[Autorisation au niveau de la propriété]
D --> E[Limitation du nombre de tentatives]
E --> F[Frontière entre l'entrée et l'exécution]
F --> G[WAF / mise à jour de la bibliothèque]
Figure 16 : conclusion finale. Les frontières de confiance se vérifient par étapes ; aucune ne doit être omise.
Références
-
IPA, Examen de spécialiste agréé en sécurité de l’information, session printemps 2024 (Reiwa 6) — Cahier de questions de l’après-midi. L’énoncé traité par cet article. ↩
-
IPA, Examen de spécialiste agréé en sécurité de l’information, session printemps 2024 (Reiwa 6) — Exemples de réponses de l’après-midi. Les exemples de réponses officiels pour chaque question. ↩
-
IPA, Examen de spécialiste agréé en sécurité de l’information, session printemps 2024 (Reiwa 6) — Commentaire de notation de l’après-midi. L’explication des taux de bonnes réponses et des tendances d’erreurs. ↩
-
NIST, SP 800-63B: Authentication and Authenticator Management. Indique le nombre de chiffres pour un secret de courte durée, la limitation du nombre de tentatives, le nombre d’échecs lors d’une nouvelle émission, et le fait de ne pas utiliser le courrier électronique pour l’authentification hors bande. ↩ ↩2
-
RFC Editor, RFC 7519: JSON Web Token (JWT). La spécification du JWT, incluant l’Unsecured JWT et
alg=none. ↩ -
RFC Editor, RFC 8725: JSON Web Token Best Current Practices. Une BCP qui définit la fixation des algorithmes autorisés, la validation de l’émetteur, du sujet et de l’audience, etc. ↩
-
OWASP, API1:2023 Broken Object Level Authorization. Explique la nécessité de vérifier l’autorisation pour chaque identifiant d’objet spécifié par l’utilisateur. ↩
-
OWASP, API3:2023 Broken Object Property Level Authorization. Explique le défaut d’autorisation au niveau de la propriété, y compris le Mass Assignment, et les mesures associées. ↩
-
Apache Logging Services, Security. Explique l’impact de la CVE-2021-44228, l’exécution de code via JNDI et LDAP, et la version corrigée. ↩
Articles associés
Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.
Examen de spécialiste agréé en sécurité de l'information — Automne 2023 (Reiwa 5), question 2 de l'après-midi expliquée — Les fichiers qui s'échappent par le Wi-Fi invité
À partir de la question 2 de l'après-midi de l'examen de spécialiste agréé en sécurité de l'information, session automne 2023 (Reiwa 5), ...
Examen de spécialiste certifié en sécurité de l'information, automne Reiwa 5, question 1 de l'après-midi — le XSS stocké qui réduit 16 avis à 2
À partir de la question 1 de l'après-midi de l'examen de spécialiste certifié en sécurité de l'information, session d'automne Reiwa 5, ce...
Ce que les clients d'un site web devraient aussi savoir — Utiliser le guide de l'IPA « Comment créer un site web sécurisé » comme checklist
Sur quels critères vérifier la sécurité du site web de votre entreprise ? Cet article explique les 11 vulnérabilités et contre-mesures co...
Top 10 des menaces de sécurité de l'information 2026 — Comment lire le classement, et ce que les PME doivent vraiment mettre en œuvre
Dans le « Top 10 des menaces de sécurité de l'information 2026 » de l'IPA, les attaques par ransomware occupent la première place pour la...
Sécurité des PME : par où commencer ? — Guide de lecture de la version 4.0 des « Directives de sécurité de l'information pour les PME » de l'IPA
Par où une PME doit-elle commencer sa démarche de sécurité ? En s'appuyant sur la version 4.0 des « Directives de sécurité de l'informati...
Sujets associés
Ces pages replacent le sujet dans un contexte plus large de services et de décisions.
Thèmes techniques Windows
Portail des sujets sur le développement Windows, l'analyse des incidents et la valorisation des actifs existants.
Services liés à ce sujet
Cet article est directement lié aux services suivants.
Création de sites web
Dans les API de membres et les intégrations avec des applications mobiles, la validation des JWT, l'autorisation au niveau de l'objet et la limitation des propriétés modifiables déterminent directement la sécurité du système web.
Conseil technique et revue de conception
Identifier les failles d'autorisation dans des API existantes, évaluer le périmètre d'impact des bibliothèques dépendantes, et élaborer des règles WAF provisoires par une revue de conception relèvent du champ du conseil technique.
Questions fréquentes
Questions souvent posées lors d’une consultation sur le sujet de cet article.
- La vérification de la signature du JWT a réussi, alors pourquoi peut-on quand même usurper l'identité d'autrui ?
- Dans ce problème, la bibliothèque de gestion des JWT acceptait la valeur de alg dans l'en-tête du JWT exactement telle que l'attaquant l'avait spécifiée, et traitait un JWT avec alg=none comme valide, sans signature. Par conséquent, même en réécrivant l'identifiant d'utilisateur dans la charge utile, la validation réussissait. La réponse de l'examen consiste à vérifier la valeur alg de l'en-tête du JWT et à confirmer qu'elle n'est pas NONE. En pratique, cependant, rejeter uniquement NONE ne suffit pas. Il faut fixer, dans la configuration côté serveur, les algorithmes autorisés (par exemple RS256), sans jamais utiliser directement l'algorithme déclaré par le jeton pour effectuer ce choix. Il faut aussi vérifier, selon l'usage, l'émetteur (issuer), l'audience, la date d'expiration, le sujet (subject), etc.
- Comparer l'identifiant d'utilisateur contenu dans le JWT avec le mid de la requête suffit-il comme mesure d'autorisation ?
- Pour la réponse attendue à cet examen, cela suffit. En vérifiant, dans le module commun P, que l'identifiant d'utilisateur contenu dans le JWT correspond au mid, on arrête une attaque qui spécifie le mid d'un autre utilisateur. Cependant, pour une API qui ne traite que les informations propres à l'appelant, il est plus sûr, en pratique, de ne jamais recevoir le mid du client et de déterminer l'identifiant d'utilisateur à partir du sujet (subject) du JWT validé. En utilisant par exemple GET /users/me ou PUT /users/me, on réduit le risque même d'oublier d'implémenter la comparaison. Une API où un administrateur agit sur un autre utilisateur doit être séparée en un point de terminaison distinct, avec sa propre politique d'autorisation.
- Pourquoi une validation ordinaire des valeurs saisies ne suffit-elle pas à empêcher l'attaque qui ajoute status ?
- Parce que même en validant la longueur de name ou la plage de age, cela ne protège rien si status — un champ qui n'aurait jamais dû être accepté — est automatiquement lié et transmis à l'objet interne. Le problème n'est pas le format de la valeur, mais l'autorisation au niveau de la propriété : l'utilisateur est-il autorisé à modifier cette propriété ? Le type de saisie utilisé pour la mise à jour ne doit définir que name et age, et rejeter toute propriété inconnue. L'état de facturation ne doit être modifié qu'à partir d'événements dignes de confiance côté serveur, comme le résultat réussi du service de paiement.
- Le code d'authentification à 4 chiffres expire au bout de 10 minutes ; pourquoi reste-t-il dangereux ?
- Parce qu'il n'existe que 10 000 candidats possibles, de 0000 à 9999, et qu'à raison de 10 tentatives par seconde, un attaquant réussit en moyenne au bout de 5 000 tentatives, soit 500 secondes. Or la durée de validité de 10 minutes équivaut à 600 secondes : en essayant des candidats non répétés dans l'ordre, il est possible d'en vérifier 6 000 dans ce laps de temps. La durée d'expiration seule ne suffit pas à arrêter une attaque par force brute. Il faut concevoir ensemble le nombre de candidats, la vitesse de tentative et le plafond du nombre de tentatives.
- La mesure attendue par la question est le verrouillage du compte ; un simple verrouillage immédiat suffit-il aussi en pratique ?
- Non. Le blanc du problème appelle un traitement qui verrouille le compte lorsque le nombre d'échecs consécutifs dépasse un seuil, mais un verrouillage permanent et figé permet à lui seul à un attaquant de verrouiller délibérément le compte d'autrui, créant un déni de service. En pratique, on combine un compteur d'échecs par compte, des délais d'attente progressifs, une évaluation du risque liée à la source ou à l'appareil, des notifications, et une procédure de récupération. Il est également important que l'émission d'un nouveau code ne remette pas le compteur d'échecs à zéro.
- Quel est l'intérêt de configurer le WAF en mode détection plutôt qu'en mode blocage ?
- Cela permet de ne pas interrompre le trafic professionnel légitime, même lorsqu'une chaîne de caractères normale est faussement jugée comme une attaque. Dans la réponse attendue, l'avantage est de pouvoir éviter un blocage causé par un faux positif, et ce qu'il faut faire est d'examiner, à chaque alerte reçue, s'il s'agit réellement d'une attaque. Le mode détection n'est pas un réglage que l'on peut laisser sans surveillance. Il sert de période d'observation pendant laquelle on consulte les journaux pour écarter les faux positifs, ajuster les règles, puis passer au blocage. En cas d'urgence, lorsqu'une vulnérabilité critique connue est effectivement exploitée, il peut être justifié de bloquer d'emblée, en mettant ce choix en balance avec le risque pour la disponibilité.
- La bibliothèque H de ce problème est-elle Log4j ?
- L'énoncé tait le nom du produit, mais la séquence d'attaque — JNDI Lookup, serveur LDAP, récupération de classe depuis un serveur HTTP, chaîne intégrée dans un en-tête HTTP, et score de base CVSS v3.1 élevé — se lit naturellement comme une abstraction de la CVE-2021-44228, connue sous le nom de Log4Shell. Cet article explique cette correspondance, mais l'examen ne demande pas de citer le nom du produit. Il peut être résolu uniquement à partir de la procédure d'attaque donnée et de la spécification du WAF.
- Que faut-il retenir de ce problème pour sa propre pratique ?
- Que réussir l'authentification, que le JWT n'a pas été falsifié, qu'on est autorisé à accéder à l'objet cible et qu'on est autorisé à modifier la propriété cible sont quatre vérifications entièrement distinctes. De plus, un code d'authentification court exige une limitation du nombre de tentatives, et une vulnérabilité critique dans une bibliothèque impose de mener de front la vérification de l'impact, la protection provisoire et la correction de fond. L'essentiel, en pratique, tient à centraliser l'autorisation dans des composants communs, à faire du schéma de saisie une liste d'autorisation, à fixer côté serveur les conditions de validation du JWT, et à connaître ses bibliothèques dépendantes de façon à pouvoir les mettre à jour.
Profil de l’auteur
Page de présentation de l’auteur de l’article.
Go Komura
Représentant de KomuraSoft LLC
Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.