Arkendia Classrooms
Leçon 18 sur 28 · 18 min

La sécurité des API REST

Quand vous ouvrez une application sur votre téléphone, l'écran que vous voyez n'est que la vitrine. Derrière, l'application échange en permanence avec des serveurs par des API : des interfaces qui répondent à des requêtes. Ces API sont devenues l'une des principales surfaces d'attaque, parce qu'elles exposent directement des données et des fonctions. Cette leçon vous apprend à lire une API du point de vue de la sécurité, à reconnaître les risques les plus fréquents et à les corriger.

Cadre légal

Interroger une API qui n'est pas la vôtre, au-delà de son usage normal et sans autorisation écrite, peut constituer un accès frauduleux à un système de traitement automatisé de données (articles 323-1 à 323-3-1 du Code pénal). Les exemples de cette leçon sont génériques et visent un laboratoire : une API de votre propre application d'entraînement, OWASP Juice Shop ou les exercices de PortSwigger Web Security Academy. Aucune charge utile offensive n'est fournie.

Comment fonctionne une API REST

Une API REST utilise le protocole HTTP, que vous connaissez, pour manipuler des ressources : des clients, des commandes, des produits. Chaque ressource a une adresse, et les verbes HTTP expriment l'action :

VerbeActionExemple (fictif)
GETLireGET /api/commandes/1042
POSTCréerPOST /api/commandes
PUT / PATCHModifierPATCH /api/commandes/1042
DELETESupprimerDELETE /api/commandes/1042

Les données voyagent en général au format JSON, et le serveur répond avec un code d'état (200 pour un succès, 401 pour « non authentifié », 403 pour « interdit », 404 pour « introuvable »). L'authentification passe souvent par un jeton transmis dans un en-tête de la requête. La documentation de HTTP est disponible sur developer.mozilla.org.

Une particularité fait la différence avec un site web classique : dans une API, c'est le client qui dit quelle ressource il veut, souvent par un simple numéro dans l'adresse. Si le serveur ne vérifie pas que ce client a le droit d'accéder à cette ressource précise, on obtient la faille la plus courante.

Le référentiel de référence : l'OWASP API Security Top 10

L'OWASP (owasp.org) tient à jour une liste des risques les plus importants spécifiques aux API. Les catégories reviennent d'une édition à l'autre ; consultez le site pour la version en vigueur. Voici les grandes familles, en langage clair :

  • Contrôle d'accès défaillant au niveau des objets : le serveur ne vérifie pas que l'utilisateur a le droit d'accéder à cette commande, ce profil. Beaucoup d'incidents réels en découlent.
  • Authentification défaillante : jetons faciles à deviner, mots de passe sans limite d'essais, jetons qui n'expirent jamais.
  • Autorisation défaillante au niveau des propriétés : l'API renvoie ou accepte plus de champs que nécessaire (par exemple, un champ « rôle » modifiable par l'utilisateur).
  • Consommation de ressources non limitée : pas de limite de requêtes, pas de plafond sur la taille des réponses, d'où risque de saturation.
  • Autorisation défaillante au niveau des fonctions : un utilisateur ordinaire peut appeler une fonction d'administration, simplement parce que personne n'a vérifié son rôle.
  • Mauvaise configuration de sécurité : messages d'erreur trop détaillés, protocoles faibles, en-têtes manquants (voir le script du chapitre 2).
  • Gestion de l'inventaire incomplète : anciennes versions de l'API, points d'accès oubliés, environnements de test exposés.
  • Consommation non sécurisée d'API tierces : on fait trop confiance aux données reçues d'un partenaire.

Étudier le risque principal : le contrôle d'accès par objet

Prenons l'exemple fictif de l'API d'Atelier Boréal : un client authentifié consulte sa commande avec GET /api/commandes/1042. Voici, en pseudo-code Python, deux versions du traitement côté serveur :

# Version fragile : on vérifie que l'utilisateur est connecté, mais pas que la commande est la sienne
def lire_commande(utilisateur, id_commande):
    commande = base.trouver_commande(id_commande)
    return commande

# Version corrigée : on vérifie la propriété de la ressource
def lire_commande(utilisateur, id_commande):
    commande = base.trouver_commande(id_commande)
    if commande is None or commande.id_client != utilisateur.id:
        return reponse(404)
    return commande

Dans la première version, le serveur répond à toute personne connectée qui demande une commande, quel que soit le propriétaire. Il suffit alors de changer le numéro dans l'adresse. Le test, dans un laboratoire d'entraînement, consiste simplement à vérifier avec deux comptes légitimes que le compte A ne peut pas lire la commande du compte B. C'est un contrôle de logique, pas une prouesse technique.

Notez le détail de la version corrigée : elle renvoie un code « introuvable » plutôt que « interdit », afin de ne pas confirmer à un curieux qu'une ressource existe. Ce choix (et son équilibre avec la clarté des messages) est une décision de conception.

Deux autres bonnes pratiques de conception renforcent la protection : utiliser des identifiants non séquentiels (aléatoires) pour compliquer l'énumération, tout en sachant que cela ne remplace jamais le contrôle d'autorisation ; et centraliser les contrôles d'accès dans un seul endroit du code pour ne pas en oublier.

La défense en profondeur pour les API

  • Authentifier fort : jetons de durée limitée, révocables ; multifacteur pour les accès sensibles.
  • Autoriser à chaque appel, ressource par ressource et fonction par fonction : « connecté » ne veut pas dire « autorisé ».
  • Valider toutes les entrées : type, longueur, format, valeurs acceptées. C'est la même discipline que celle de votre script du chapitre 2.
  • Limiter les réponses : ne renvoyer que les champs nécessaires, jamais l'objet complet de la base.
  • Limiter la fréquence des requêtes, et la taille des réponses.
  • Chiffrer en transit (HTTPS partout).
  • Gérer des messages d'erreur sobres : pas de trace technique pour l'utilisateur, tout dans les journaux.
  • Maintenir un inventaire : une liste à jour des versions et des points d'accès, avec la fin de vie programmée des anciennes versions.
  • Journaliser et surveiller : une suite de requêtes qui parcourent des numéros consécutifs est un comportement typique à détecter dans votre SIEM.

Comment tester une API dans un cadre légal

Dans votre laboratoire, un proxy d'interception comme Burp Suite, mentionné dans le cours précédent, permet d'observer les requêtes entre un navigateur et une application d'entraînement. Un test d'API suit une démarche classique :

  1. Cartographier : lister les points d'accès, les verbes, les paramètres (la documentation et le trafic observé aident).
  2. Comprendre les rôles : qui est censé accéder à quoi ? Créer deux comptes de test de même niveau et un compte administrateur.
  3. Vérifier les autorisations : le compte A accède-t-il aux ressources de B ? Un compte ordinaire peut-il appeler les fonctions d'administration ?
  4. Vérifier les entrées et les limites : comment l'API réagit-elle à des valeurs inattendues ? Y a-t-il des limites de fréquence ?
  5. Documenter : requête, réponse, constat, risque, recommandation.

Dans un audit réel, ces tests s'effectuent sur un environnement convenu (idéalement de pré-production), avec des comptes de test fournis par le client, et dans la fenêtre horaire autorisée.

À vous de jouer

Voici le tableau d'inspection d'une API fictive. Pour chaque ligne, indiquez le risque OWASP probable et la correction :

  1. GET /api/clients/77 renvoie la fiche du client 77 à n'importe quel utilisateur connecté.
  2. POST /api/connexion accepte un nombre illimité d'essais de mot de passe.
  3. GET /api/v1/utilisateurs est une ancienne version, oubliée, qui existe encore et ne contrôle plus les droits.
  4. La réponse d'un profil inclut un champ mot_de_passe_hache et un champ role que l'utilisateur peut modifier par PATCH.
Voir une solution
  1. Contrôle d'accès défaillant au niveau des objets. Correction : vérifier, côté serveur, que l'utilisateur a le droit d'accéder à ce client.
  2. Authentification défaillante / absence de limitation. Correction : limiter le nombre d'essais, temporiser, ajouter un facteur supplémentaire et alerter sur les échecs répétés.
  3. Gestion de l'inventaire incomplète. Correction : lister toutes les versions, retirer ou désactiver les anciennes, et ne conserver que les versions maintenues.
  4. Autorisation défaillante au niveau des propriétés (exposition excessive de données et modification d'un champ sensible). Correction : ne renvoyer que les champs nécessaires (jamais le hachage d'un mot de passe) et interdire la modification du rôle par l'utilisateur lui-même.

Pour votre checklist

Ajoutez une section « API » avec ces questions : l'autorisation est-elle vérifiée sur chaque ressource et chaque fonction ? Y a-t-il une limitation de fréquence ? Les réponses sont-elles minimales ? L'inventaire des versions est-il à jour ?

À retenir

  • Une API REST expose des ressources par adresse et verbe HTTP ; c'est le client qui désigne la ressource, d'où l'importance de l'autorisation.
  • Le risque n°1 : le contrôle d'accès défaillant par objet. « Connecté » ne signifie pas « autorisé ».
  • Défendre une API : authentification robuste, autorisation à chaque appel, validation des entrées, réponses minimales, limitation de fréquence, inventaire, journalisation.
  • Les tests se font dans un laboratoire, ou dans un périmètre convenu par écrit, avec des comptes de test.
  • L'OWASP API Security Top 10 (owasp.org) est la référence à consulter.
Publicité
Connectez-vous pour enregistrer votre progression. Se connecter