Chapitre 1317 minIntermédiaire
API REST à l'examen : E6 et E7 SLAM, cas corrigé
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- préparer ta réalisation d'API pour l'E6 ;
- reconnaître les questions d'API des sujets d'E7 ;
- répondre en quatre temps : situer, nommer, expliquer, corriger ;
- traiter un cas complet d'API vulnérable.
7 exercices corrigés
Au BTS SIO SLAM, les API REST sont évaluées deux fois : à l'E6, où tu fais évoluer en une heure une application que tu as construite, souvent une API ; et à l'E7, l'écrit de cybersécurité, où tu dois lire un échange HTTP ou un journal, choisir un code de statut, distinguer authentification et autorisation, et corriger une route vulnérable. Ce chapitre rassemble ce qui est attendu à chaque épreuve, les questions types tirées des sujets officiels, une méthode de réponse et un cas complet corrigé.
À l'E6 : une API que tu sais faire évoluer
À l'E6 SLAM, la commission choisit l'une de tes deux réalisations et te remet une expression des besoins : une évolution ou une correction. Tu as 30 minutes pour l'analyser, 20 minutes pour expliquer ta démarche, une heure pour la réaliser, puis 20 minutes pour la montrer et répondre aux questions (le déroulement complet est dans le cours E6 SLAM). Quand ta réalisation est une API, ou un front qui en consomme une, les évolutions demandées ressemblent souvent à ceci :
| Évolution demandée | Ce que tu modifies | Chapitre utile |
|---|---|---|
| « Les comptables doivent pouvoir filtrer les fiches par région » | Un paramètre de la chaîne de requête, validé par liste blanche | 6 |
| « Seul le responsable peut valider » | Un contrôle de rôle sur une route, 403 sinon | 9, 10 |
| « Une fiche validée ne doit plus être modifiable » | Une règle d'état, 409 sinon | 3, 6 |
| « La liste devient trop longue » | La pagination, avec une limite plafonnée | 6 |
| « Ajouter le motif du refus » | Un champ dans la représentation, une colonne en base, la documentation | 6, 7 |
| « Les erreurs de saisie doivent s'afficher sous chaque champ » | Des erreurs 422 au format RFC 9457 avec la liste des champs | 6 |
Pour être prêt le jour J, ta réalisation doit déjà avoir : une documentation OpenAPI à jour (ou au minimum un tableau des routes dans le README), une collection de requêtes Bruno ou Postman, ou un fichier .http, qui démontre chaque route en quelques secondes, au moins un test automatisé qui tourne, et un jeu de données avec deux utilisateurs de rôles différents. Pendant la recette, montre la requête et la réponse, code de statut compris, et montre aussi un refus : un 401 sans jeton, un 403 avec le mauvais rôle, un 409 sur une fiche validée. Prouver que ton API dit non quand il faut vaut autant que prouver qu'elle dit oui.
À l'E7 : les questions qui reviennent
Les sujets d'E7 SLAM (et d'U6 jusqu'en 2024) posent des questions d'API presque chaque année. Le tableau suivant les classe d'après les sujets officiels ; les références renvoient au cours E7 SLAM, qui les détaille.
| Question type | Sujet officiel | Ce qu'il faut répondre |
|---|---|---|
Pourquoi DELETE et PUT sont-elles dangereuses ? | 2025, Casterman | Elles modifient ou suppriment des données existantes : atteinte à l'intégrité et à la disponibilité, parfois irréversible ; elles exigent une authentification et un contrôle d'autorisation |
| Ajouter le contrôle d'autorisation dans la méthode d'une API | 2025, Casterman | Vérifier le rôle (et le propriétaire) avant l'action, renvoyer 403 (ou 404) sinon, et ne rien modifier |
| API publique en lecture : comment l'ouvrir ? | 2025, Casterman | Une API publique séparée, en lecture seule, plutôt que d'ouvrir l'API authentifiée : les routes de modification n'y existent pas |
| HTTPS, clé d'API ou jeton : qui authentifie qui ? | 2025, Kirassur (Nouvelle-Calédonie) | La clé identifie l'application partenaire et reste sur son serveur ; le jeton, temporaire et limité, est remis à l'utilisateur ; HTTPS protège le transport |
| Un journal montre des identifiants qui s'incrémentent | 2025, Kirassur | Contrôle d'accès à l'objet défaillant (BOLA) : vérifier le propriétaire à chaque requête, identifiants non prévisibles en complément, limitation de débit |
Une API répond sans authentification ; invalid_token | 2024, GSIC (U6) | Exiger un jeton sur chaque route ; un invalid_token après une pause signale un jeton d'accès expiré, à rafraîchir |
| Choisir le code de statut | Presque tous les sujets | 401 : non authentifié ; 403 : authentifié mais non autorisé ; 404 : introuvable ; 409 : conflit d'état ; 429 : trop de requêtes |
En 2026 (cas NDV), le sujet ne portait pas sur une API, mais son dossier B testait les mêmes réflexes sur un site web : contrôle d'accès à chaque objet, requêtes préparées, hachage des mots de passe.
Les corrigés des sujets 2025 Casterman et 2024 GSIC sont publiés sur le blog du site.
La méthode pour répondre
Une question d'API à l'E7 se traite en quatre temps.
- Situer : quelle ressource, quelle méthode, quel acteur ? Relis le document (le code, le journal, la documentation) et repère la ligne qui pose problème. Cite-la : « ligne 4 du document B3 ».
- Nommer : donne le nom exact de la notion ou de la faille. « Contrôle d'accès à l'objet défaillant (BOLA, API1 de l'OWASP API Security Top 10) » vaut plus que « il y a un problème de sécurité ».
- Expliquer la conséquence dans le contexte : quelles données, quel acteur, quel critère de sécurité (disponibilité, intégrité, confidentialité, preuve). « Un abonné peut lire les trajets des autres abonnés : atteinte à la confidentialité de données personnelles de localisation. »
- Corriger : la mesure précise, et le code si la question le demande. Une correction de code se juge sur sa justesse, pas sur sa longueur : la condition qui manque, au bon endroit.
Cas corrigé : l'API du réseau Vélivert
Contexte. Vélivert est le service de vélos en libre-service d'une métropole : 3 200 vélos, 180 stations et 41 000 abonnés. Une API REST, développée en PHP avec PDO, sert l'application mobile des abonnés. Tu es développeur dans l'équipe qui maintient l'API. L'office de tourisme souhaite afficher la disponibilité des vélos sur son site.
Document 1 : extrait du journal d'accès du serveur web (adresses IP, date, requête, code, taille de la réponse en octets).
198.51.100.23 [02/11/2026:18:04:11] "GET /api/v1/abonnes/20417/trajets HTTP/1.1" 200 6120
198.51.100.23 [02/11/2026:18:04:11] "GET /api/v1/abonnes/20418/trajets HTTP/1.1" 200 4388
198.51.100.23 [02/11/2026:18:04:12] "GET /api/v1/abonnes/20419/trajets HTTP/1.1" 200 15204
198.51.100.23 [02/11/2026:18:04:12] "GET /api/v1/abonnes/20420/trajets HTTP/1.1" 200 2961
198.51.100.23 [02/11/2026:18:04:12] "GET /api/v1/abonnes/20421/trajets HTTP/1.1" 200 8840
203.0.113.9 [02/11/2026:18:05:40] "GET /api/v1/abonnes/20417/trajets HTTP/1.1" 403 41
Document 2 : la méthode de l'API qui renvoie les trajets d'un abonné.
1 // Route : GET /api/v1/abonnes/{id}/trajets
2 public function trajetsAbonne(int $idAbonne): void
3 {
4 $utilisateur = $this->verifierJeton($this->lireJetonBearer()); // null si absent, invalide ou expiré
5 if ($utilisateur === null) {
6 $this->repondre(403, ['erreur' => 'Accès refusé']);
7 return;
8 }
9 $sql = "SELECT t.id, t.date_depart, t.station_depart, t.station_arrivee, t.duree_min,
10 a.nom, a.prenom, a.telephone, a.adresse
11 FROM trajet t JOIN abonne a ON a.id = t.id_abonne
12 WHERE t.id_abonne = " . $idAbonne . " ORDER BY t.date_depart DESC";
13 $trajets = $this->pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
14 $this->repondre(200, $trajets);
15 }
La méthode verifierJeton renvoie un tableau associatif qui décrit l'utilisateur connecté (id, role), ou null. La méthode repondre envoie le code de statut et le corps en JSON.
Exercice 1 sur 7 Question 1 : le code de statut Réussi À revoir
Ligne 6 du document 2, la méthode renvoie 403 quand le jeton est absent, invalide ou expiré. Ce code est-il adapté ? Justifie, et indique ce que l'application mobile devrait faire en recevant le bon code.
Voir la réponseMasquer la réponse de l'exercice 1
Réponse modèle. Non. Le code 403 Forbidden signifie que l'utilisateur est authentifié mais n'a pas le droit d'accéder à la ressource. Ici, l'API ne sait pas qui appelle : le jeton est absent, invalide ou expiré. Le code adapté est 401 Unauthorized, accompagné de l'en-tête WWW-Authenticate: Bearer. En recevant 401, l'application mobile tente d'obtenir un nouveau jeton d'accès avec son jeton de rafraîchissement, et à défaut redemande à l'abonné de se connecter. Avec 403, elle croirait à un refus définitif et afficherait un message trompeur.
Tu avais trouvé ?
Exercice 2 sur 7 Question 2 : analyser le journal Réussi À revoir
Analyse le document 1. Que fait le client 198.51.100.23 ? Quelle faille de l'API son comportement révèle-t-il ? Nomme-la et explique-la en t'appuyant sur le document 2.
Voir la réponseMasquer la réponse de l'exercice 2
Réponse modèle. Le client 198.51.100.23 envoie en une seconde cinq requêtes sur des identifiants d'abonnés consécutifs (20417 à 20421) et obtient chaque fois 200 avec des réponses de tailles différentes, donc de vraies données : il énumère les abonnés et récupère leurs trajets. Un abonné connecté ne peut être qu'un seul de ces abonnés : l'API renvoie donc les trajets d'autres abonnés.
La faille est un contrôle d'accès à l'objet défaillant (Broken Object Level Authorization, BOLA, risque API1:2023 de l'OWASP API Security Top 10). Le document 2 vérifie l'authentification (lignes 4 à 8 : un jeton valide est exigé) mais jamais l'autorisation : aucune ligne ne compare $idAbonne à l'identifiant de l'utilisateur connecté ($utilisateur['id']). Tout abonné authentifié lit les trajets de tous les autres.
La dernière ligne du journal montre un appel sans jeton valide, refusé : l'authentification fonctionne, ce qui confirme que c'est bien l'autorisation qui manque.
Tu avais trouvé ?
Exercice 3 sur 7 Question 3 : les conséquences Réussi À revoir
Indique le critère de sécurité atteint et les conséquences pour Vélivert, en tenant compte de la nature des données.
Voir la réponseMasquer la réponse de l'exercice 3
Réponse modèle. Le critère atteint est la confidentialité. Les données exposées sont des données personnelles au sens du RGPD : nom, prénom, téléphone, adresse, et surtout l'historique des déplacements (stations, dates, heures), qui révèle les habitudes de vie, le domicile et le lieu de travail des abonnés. Pour Vélivert, c'est une violation de données personnelles : elle doit être documentée dans le registre des violations et notifiée à la CNIL dans les 72 heures, puisqu'elle est susceptible d'engendrer un risque pour les personnes ; et comme ce risque est élevé (localisation, coordonnées), les abonnés concernés doivent aussi être informés. S'y ajoutent l'atteinte à l'image du service et un risque de sanction. Les obligations en cas de violation sont détaillées dans le cours Droit du numérique et RGPD.
Tu avais trouvé ?
Exercice 4 sur 7 Question 4 : corriger la méthode Réussi À revoir
Réécris les lignes 4 à 14 du document 2 pour corriger toutes les failles que tu identifies. Un abonné ne peut consulter que ses propres trajets ; un utilisateur de rôle support peut consulter ceux de tout abonné. L'application n'affiche que la date, les stations et la durée de chaque trajet.
Voir la réponseMasquer la réponse de l'exercice 4
4 $utilisateur = $this->verifierJeton($this->lireJetonBearer());
5 if ($utilisateur === null) {
6 header('WWW-Authenticate: Bearer realm="api"');
7 $this->repondre(401, ['erreur' => 'Authentification requise']);
8 return;
9 }
10 $estSupport = $utilisateur['role'] === 'support';
11 if (!$estSupport && $utilisateur['id'] !== $idAbonne) {
12 $this->repondre(404, ['erreur' => 'Abonné introuvable']);
13 return;
14 }
15 $sql = "SELECT id, date_depart, station_depart, station_arrivee, duree_min
16 FROM trajet WHERE id_abonne = :id
17 ORDER BY date_depart DESC LIMIT 50";
18 $requete = $this->pdo->prepare($sql);
19 $requete->execute(['id' => $idAbonne]);
20 $this->repondre(200, $requete->fetchAll(PDO::FETCH_ASSOC));
Les corrections, et pourquoi :
401au lieu de403quand il n'y a pas de jeton valide, avecWWW-Authenticate(question 1) ;- le contrôle d'autorisation (lignes 10 à 14) : un abonné ne passe que si l'identifiant demandé est le sien ; le rôle
supportpasse toujours. Le code404évite de confirmer l'existence de l'abonné demandé ;403serait aussi accepté. La comparaison!==suppose queverifierJetonrenvoie unidde type entier, comme$idAbonne; - la minimisation des données : la jointure avec
abonneet les colonnesnom,prenom,telephone,adressedisparaissent. L'application n'en a pas besoin, et les renvoyer était une exposition excessive de propriétés (BOPLA, API3:2023) contraire au principe de minimisation du RGPD ; - la requête préparée : le paramètre typé
intrendait l'injection impossible ici, mais la concaténation reste une mauvaise pratique qui deviendrait une faille au premier changement de type. La requête préparée est la norme (injection SQL) ; - une limite (
LIMIT 50) : un abonné ancien peut avoir des milliers de trajets ; sans limite, la réponse grossit sans fin (API4:2023). Une vraie pagination (?page=, ou un curseur surdate_depart) compléterait la correction.
Tu avais trouvé ?
Exercice 5 sur 7 Question 5 : des mesures complémentaires Réussi À revoir
En plus de la correction du code, propose deux mesures qui auraient limité l'attaque ou permis de la détecter plus tôt.
Voir la réponseMasquer la réponse de l'exercice 5
Réponse modèle. Deux mesures parmi :
- une limitation de débit par jeton et par adresse IP (par exemple 60 requêtes par minute sur cette route), avec une réponse
429 Too Many Requests: l'énumération aurait été fortement ralentie ; - des identifiants non prévisibles (UUID) dans les URI à la place des entiers consécutifs : on ne devine plus l'identifiant suivant. C'est une défense complémentaire, qui ne remplace pas le contrôle d'autorisation ;
- une alerte sur le journal : un même jeton qui accède à des ressources de plusieurs abonnés différents en quelques secondes déclenche une alerte, voire le blocage automatique de l'adresse IP (un outil comme fail2ban, ou un pare-feu applicatif) ;
- des tests d'autorisation automatisés : pour chaque route qui prend un identifiant, un test vérifie qu'un abonné A n'accède pas aux données de l'abonné B.
Tu avais trouvé ?
Exercice 6 sur 7 Question 6 : ouvrir l'API à l'office de tourisme Réussi À revoir
L'office de tourisme veut afficher sur son site le nombre de vélos disponibles dans chaque station, mis à jour toutes les minutes. Un développeur propose de lui donner un compte abonné et son jeton. Critique cette proposition et propose une solution.
Voir la réponseMasquer la réponse de l'exercice 6
Réponse modèle. La proposition est à rejeter. Un jeton d'abonné donne accès aux routes de l'API des abonnés (profil, trajets, paiement) alors que l'office n'a besoin que de la disponibilité des stations : c'est contraire au moindre privilège. Le jeton, utilisé par le site de l'office, risque aussi d'être exposé dans son code, et il expire : il faudrait une connexion automatisée avec un mot de passe stocké chez l'office.
La disponibilité des stations n'est pas une donnée personnelle et n'a rien de confidentiel. Il vaut mieux créer une route publique en lecture seule, séparée de l'API des abonnés, par exemple GET /api/v1/stations, qui renvoie pour chaque station son identifiant, son nom, sa position et le nombre de vélos et de places disponibles. Elle n'a aucune route de modification. On la protège par une limitation de débit, et par une mise en cache d'une minute (Cache-Control: public, max-age=60) qui correspond au besoin et allège le serveur. Si Vélivert veut suivre l'usage par partenaire, elle peut attribuer à l'office une clé d'API limitée à cette route, utilisée depuis le serveur de l'office et jamais dans le code de ses pages.
Tu avais trouvé ?
Exercice 7 sur 7 Question 7 : HTTPS suffit-il ? Réussi À revoir
Le responsable de l'équipe affirme : « Notre API est en HTTPS, elle est donc sécurisée. » Réponds-lui en trois ou quatre phrases.
Voir la réponseMasquer la réponse de l'exercice 7
Réponse modèle. HTTPS chiffre les échanges entre l'application et l'API : il garantit la confidentialité et l'intégrité des données pendant le transport et authentifie le serveur, ce qui empêche le vol du jeton sur le réseau. Mais il ne contrôle pas ce que l'API accepte de faire : l'attaque du document 1 a eu lieu en HTTPS, avec un jeton valide. La sécurité d'une API repose aussi sur l'authentification de chaque requête, le contrôle d'autorisation sur chaque objet, la minimisation des données renvoyées, la validation des entrées et la limitation de débit. HTTPS est nécessaire, pas suffisant.
Tu avais trouvé ?
Les erreurs qui coûtent des points
| Erreur | Pourquoi elle coûte | À la place |
|---|---|---|
| « Il faut sécuriser l'API » | Trop vague : aucune mesure, aucun mécanisme | Nommer la mesure : contrôle du propriétaire, 401, limitation de débit… |
Confondre 401 et 403 | Erreur de notion, systématiquement relevée | 401 : qui es-tu ? 403 : je sais, et c'est non |
| « HTTPS protège contre cette attaque » | Faux dès que l'attaquant est un utilisateur légitime | HTTPS protège le transport, pas les droits |
| « CORS empêche les appels non autorisés » | CORS ne concerne que les navigateurs | L'authentification et l'autorisation protègent l'API |
| Corriger la faille côté front (« cacher le bouton ») | Un client modifié ou curl appelle l'API directement | Le contrôle se fait dans l'API |
| « Utiliser des UUID » comme seule correction d'une BOLA | Un identifiant finit toujours par fuiter | UUID en complément du contrôle du propriétaire |
| Un JWT « chiffré » | La charge utile d'un JWT signé est lisible par tous | Un JWT est signé : il est infalsifiable, pas secret |
| Une réponse sans lien avec le document | Le correcteur attend que tu exploites le dossier | Citer la ligne, l'heure du journal, le nom de la route |
La fiche réflexe de la veille
- Méthodes :
GETlit (sûre),POSTcrée (non idempotente),PUTremplace,PATCHmodifie en partie,DELETEsupprime ;PUTetDELETEsont idempotentes. - Codes :
200,201+Location,204;400,401,403,404,409,422,429;500,502,503. - URI : des noms au pluriel, pas de verbe ; les filtres dans la chaîne de requête ; la version dans le chemin (
/v1). - Erreurs :
application/problem+json(RFC 9457), jamais de trace technique côté client. - Authentification : clé d'API pour une application, jeton court pour un utilisateur, OAuth 2.0 pour un accès délégué ; jamais de secret dans un front ou une application mobile ; jamais de jeton dans une URL.
- JWT : en-tête, charge utile, signature ; lisible par tous ;
verify(signature, algorithme,exp), jamaisdecode. - OWASP API Top 10 (2023) : API1 BOLA, API2 authentification, API3 propriétés (BOPLA), API4 consommation de ressources, API5 fonctions (BFLA), API9 inventaire.
- CORS : une permission du navigateur, pas une protection de l'API ; liste blanche des origines, jamais l'origine reçue renvoyée avec les cookies.
- Limitation de débit :
429+Retry-After, par jeton, clé ou adresse IP.