Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

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éeCe que tu modifiesChapitre utile
« Les comptables doivent pouvoir filtrer les fiches par région »Un paramètre de la chaîne de requête, validé par liste blanche6
« Seul le responsable peut valider »Un contrôle de rôle sur une route, 403 sinon9, 10
« Une fiche validée ne doit plus être modifiable »Une règle d'état, 409 sinon3, 6
« La liste devient trop longue »La pagination, avec une limite plafonnée6
« Ajouter le motif du refus »Un champ dans la représentation, une colonne en base, la documentation6, 7
« Les erreurs de saisie doivent s'afficher sous chaque champ »Des erreurs 422 au format RFC 9457 avec la liste des champs6

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 typeSujet officielCe qu'il faut répondre
Pourquoi DELETE et PUT sont-elles dangereuses ?2025, CastermanElles 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 API2025, CastermanVé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, CastermanUne 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émentent2025, KirassurContrô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_token2024, 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 statutPresque tous les sujets401 : 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.

  1. 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 ».
  2. 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é ».
  3. 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. »
  4. 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).

text
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é.

php
 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
php
 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 :

  • 401 au lieu de 403 quand il n'y a pas de jeton valide, avec WWW-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 support passe toujours. Le code 404 évite de confirmer l'existence de l'abonné demandé ; 403 serait aussi accepté. La comparaison !== suppose que verifierJeton renvoie un id de type entier, comme $idAbonne ;
  • la minimisation des données : la jointure avec abonne et les colonnes nom, prenom, telephone, adresse disparaissent. 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é int rendait 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 sur date_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

ErreurPourquoi elle coûteÀ la place
« Il faut sécuriser l'API »Trop vague : aucune mesure, aucun mécanismeNommer la mesure : contrôle du propriétaire, 401, limitation de débit…
Confondre 401 et 403Erreur de notion, systématiquement relevée401 : 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égitimeHTTPS protège le transport, pas les droits
« CORS empêche les appels non autorisés »CORS ne concerne que les navigateursL'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 directementLe contrôle se fait dans l'API
« Utiliser des UUID » comme seule correction d'une BOLAUn identifiant finit toujours par fuiterUUID en complément du contrôle du propriétaire
Un JWT « chiffré »La charge utile d'un JWT signé est lisible par tousUn JWT est signé : il est infalsifiable, pas secret
Une réponse sans lien avec le documentLe correcteur attend que tu exploites le dossierCiter la ligne, l'heure du journal, le nom de la route

La fiche réflexe de la veille

  • Méthodes : GET lit (sûre), POST crée (non idempotente), PUT remplace, PATCH modifie en partie, DELETE supprime ; PUT et DELETE sont 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), jamais decode.
  • 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.

Chapitre terminé !

Fais les 7 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Cours suivant : Node.js et ExpressRevoir le sommaire du cours