Aller au contenu
Réussir mon BTS SIO

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

Par exemple

Chapitre 1319 minIntermédiaire

Sécuriser une API REST et une application mobile

Par Josselyn C., prof de BTS SIO. Mis à jour le .

À la fin de ce chapitre, tu sauras

  • classer une faille d'API dans l'OWASP API Security Top 10 ;
  • filtrer les objets et les propriétés renvoyés par une API ;
  • limiter les ressources qu'une API consomme ;
  • stocker un secret sur un téléphone sans l'exposer ;
  • expliquer les précautions de la biométrie sur mobile.

4 exercices corrigés1 schéma

Une API REST expose directement les données et les fonctions d'une application à d'autres programmes : front web, application mobile, services partenaires. Sa sécurité repose sur l'authentification de chaque appel, le contrôle d'accès à chaque objet et à chaque propriété, et la limitation des ressources consommées. Ce chapitre suit l'OWASP API Security Top 10 (édition 2023), puis présente les risques propres aux applications mobiles (OWASP Mobile Top 10, édition 2024) : stockage sur le téléphone, communication, authentification biométrique.

OWASP API Security Top 10 (2023)

Les API ont leurs propres failles dominantes, d'où une liste dédiée de l'OWASP.

CodeRisqueEn une phraseExemple dans l'API d'AppliFrais
API1:2023Broken Object Level Authorization (BOLA)Pas de vérification du droit sur l'objet demandéGET /api/fiches/1188 renvoie la fiche d'un autre visiteur
API2:2023Broken AuthenticationAuthentification contournable ou jetons mal gérésJeton JWT sans expiration, signature non vérifiée
API3:2023Broken Object Property Level AuthorizationPas de contrôle propriété par propriété, en lecture comme en écritureLa réponse contient mdp_hash ; le visiteur peut modifier role
API4:2023Unrestricted Resource ConsumptionAucune limite de débit, de taille ou de volumeGET /api/fiches?limite=1000000
API5:2023Broken Function Level AuthorizationFonctions d'administration accessibles à tousUn visiteur appelle DELETE /api/utilisateurs/12
API6:2023Unrestricted Access to Sensitive Business FlowsUn processus métier automatisable à l'excèsUn script crée des centaines de demandes d'avance
API7:2023Server Side Request ForgeryL'API télécharge une URL fournie sans la contrôlerImport d'un justificatif par URL (chapitre 3)
API8:2023Security MisconfigurationRéglages dangereuxCORS ouvert à tous, traces d'erreur renvoyées au client
API9:2023Improper Inventory ManagementVersions et routes oubliéesL'ancienne /api/v1/, sans contrôle d'accès, tourne encore
API10:2023Unsafe Consumption of APIsConfiance aveugle dans une API tierceLes données d'un service partenaire sont insérées en base sans validation

Authentifier chaque appel

Une API n'a pas de « page de connexion » qui protège le reste : chaque requête doit prouver son identité, et chaque route doit être protégée par défaut (refuser par défaut).

MécanismeUsagePrécautions
Jeton JWT ou jeton opaque dans Authorization: Bearer …Un utilisateur, via un front ou une application mobileExpiration courte, signature et algorithme vérifiés (chapitre 11)
Clé d'API dans un en-tête (X-API-Key)Un programme partenaire, de serveur à serveurUne clé par partenaire, droits limités, révocable, stockée hachée côté serveur, jamais dans une application mobile ou un front
OAuth 2.0 (jeton d'accès avec scopes)Accès délégué au nom d'un utilisateur, par une application tierceDroits minimaux, durée courte
Certificat client (TLS mutuel)Échanges entre services d'une même organisationGestion des certificats

Un jeton ou une clé ne passe jamais dans l'URL (?token=…) : les URL sont enregistrées dans les journaux des serveurs et des proxys, dans l'historique du navigateur, et transmises dans l'en-tête Referer. Et tout passe en HTTPS.

BOLA et BOPLA : filtrer les objets et les propriétés

BOLA est le nom de l'IDOR dans le monde des API : on corrige en vérifiant le propriétaire à chaque accès, comme au chapitre 3.

BOPLA touche les propriétés d'un objet. En lecture, l'API renvoie plus de champs que nécessaire, en comptant sur le front pour ne pas les afficher :

js
// VULNÉRABLE : renvoie toute la ligne, y compris mdp_hash
router.get('/api/moi', authentifier, async (req, res) => {
  const [lignes] = await pool.execute('SELECT * FROM utilisateur WHERE id = ?', [req.utilisateur.id]);
  res.json(lignes[0]);
});

L'outil de développement du navigateur montre la réponse complète : l'empreinte du mot de passe, des champs internes. La correction sélectionne explicitement les colonnes, ou passe par un objet de réponse dédié (un DTO, Data Transfer Object) :

js
const [lignes] = await pool.execute('SELECT id, login, email, role FROM utilisateur WHERE id = ?', [req.utilisateur.id]);
res.json(lignes[0]);

En écriture, c'est l'affectation de masse : l'API accepte des champs que l'utilisateur ne doit pas modifier (role, actif, montant_valide). La parade est la liste blanche des champs modifiables, par exemple avec un schéma de validation (chapitre 7).

Limiter la consommation de ressources

Une API sans limites se fait épuiser : par un attaquant, par un script mal écrit, ou par la facture d'un service payant appelé à chaque requête (envoi de SMS, géocodage). Les limites à poser :

RessourceLimite
Nombre de requêtesPar client (adresse IP, jeton, clé d'API) et par fenêtre de temps ; plus stricte sur la connexion, l'envoi de mails ou de SMS. Réponse 429 Too Many Requests
Taille des requêtesCorps JSON (express.json({ limit: '100kb' })), fichiers envoyés (client_max_body_size dans nginx, option limits de multer)
Taille des réponsesPagination obligatoire avec un maximum : ?page=2&limite=50, et limite plafonnée côté serveur
DuréeDélai maximal des requêtes vers la base et vers les services tiers
Opérations coûteusesGénération de PDF, export, recherche plein texte : files d'attente et quotas
js
const limite = Math.min(Number.parseInt(req.query.limite, 10) || 20, 100);   // 20 par défaut, 100 au maximum
const page = Math.max(Number.parseInt(req.query.page, 10) || 1, 1);
const decalage = (page - 1) * limite;
// query : avec execute, mysql2 refuse des nombres pour LIMIT ? sur MySQL 8 ; les valeurs restent échappées
const [fiches] = await pool.query(
  'SELECT id, mois, etat, montant_valide FROM fiche_frais WHERE id_visiteur = ? ORDER BY mois DESC LIMIT ? OFFSET ?',
  [req.utilisateur.id, limite, decalage]
);

limite et decalage sont des entiers calculés par le serveur, bornés : impossible de demander un million de lignes.

Méthodes HTTP et codes de retour

Chaque méthode a un sens, et les méthodes qui modifient (POST, PUT, PATCH, DELETE) appellent les contrôles les plus stricts. Le sujet E7 SLAM 2025 demandait justement pourquoi DELETE et PUT sont dangereuses : elles modifient ou suppriment des données existantes, ce qui atteint l'intégrité et la disponibilité, parfois sans retour possible (corrigé du sujet 2025).

CodeSensExemple
200 OK, 201 Created, 204 No ContentSuccèsLecture, création, suppression
400 Bad RequestRequête invalideChamp manquant, type incorrect
401 UnauthorizedNon authentifiéJeton absent ou expiré
403 ForbiddenAuthentifié, non autoriséMauvais rôle
404 Not FoundIntrouvableOu ressource d'un autre, qu'on ne veut pas révéler
405 Method Not AllowedMéthode non prévue sur cette routeDELETE /api/fiches
409 ConflictÉtat incompatibleValider une fiche déjà validée
429 Too Many RequestsLimite de débit dépasséeTrop de tentatives
500 Internal Server ErrorErreur serveurMessage générique, détail dans le journal

Une route qui n'existe que pour une méthode ne doit pas en accepter d'autres : un routeur bien configuré répond 404 ou 405 aux méthodes non déclarées.

Inventorier les versions et les routes

Une API évolue : /api/v1/, puis /api/v2/. L'ancienne version, conservée « pour les vieux clients », n'a souvent pas reçu les corrections de sécurité de la nouvelle. Une route de test, une route de débogage ou une documentation interactive (Swagger UI) oubliée en production sont autant de portes ouvertes.

Les bonnes pratiques : une documentation OpenAPI à jour, qui sert d'inventaire de toutes les routes ; une date de fin annoncée pour chaque ancienne version, puis sa suppression ; pas de route de débogage ni de documentation interactive exposée en production sans authentification ; un environnement de préproduction qui ne partage pas les données de production. Le cours JavaScript et Node.js montre la construction d'une API REST avec Express.

Consommer une API tierce sans lui faire confiance

L'API d'AppliFrais appelle un service partenaire de calcul des indemnités kilométriques. Les données qu'il renvoie sont des entrées comme les autres : elles peuvent être fausses, malveillantes si le partenaire est compromis, ou absentes s'il est en panne.

  • Valider la réponse (types, plages, format) avant de l'utiliser ou de l'enregistrer.
  • Encoder à l'affichage ce qui vient du tiers, comme une saisie d'utilisateur.
  • Appeler le tiers en HTTPS, avec un délai maximal, et prévoir le comportement en cas de panne (échouer proprement, chapitre 12).
  • Ne pas suivre aveuglément les redirections renvoyées par le tiers.

Les risques des applications mobiles

Une application mobile tourne sur un appareil que l'éditeur ne contrôle pas : le téléphone peut être perdu, volé, partagé, modifié (rooté ou jailbreaké), et le code de l'application peut être décompilé. L'OWASP Mobile Top 10 (édition 2024) liste les risques dominants :

CodeRisqueExemple
M1Improper Credential Usage : identifiants mal utilisésClé d'API écrite dans le code de l'application, mot de passe stocké en clair
M2Inadequate Supply Chain SecurityKit de développement tiers vulnérable ou malveillant
M3Insecure Authentication/AuthorizationContrôle des droits fait seulement dans l'application, pas sur le serveur
M4Insufficient Input/Output ValidationInjection SQL dans la base SQLite locale
M5Insecure CommunicationÉchanges en HTTP, certificat non vérifié
M6Inadequate Privacy ControlsCollecte excessive de données personnelles, journaux bavards
M7Insufficient Binary ProtectionsApplication facile à décompiler et à modifier
M8Security MisconfigurationMode débogage actif dans la version publiée, sauvegarde des données autorisée
M9Insecure Data StorageDonnées sensibles en clair sur l'appareil
M10Insufficient CryptographyAlgorithmes faibles, clés codées en dur

Pour vérifier une application mobile, l'OWASP publie aussi le MASVS (exigences de sécurité) et le MASTG (guide de test), l'équivalent mobile de l'ASVS et du WSTG.

Stocker des données sur le téléphone

Aucun secret dans le code de l'application. Un fichier APK (Android) se décompile en quelques minutes avec des outils gratuits comme jadx : une clé d'API ou un mot de passe écrits dans le code sont lisibles par tous. Une application mobile est un client public : elle ne peut pas garder de secret. Les appels qui exigent une clé privée passent par ton serveur, qui détient la clé.

Les données sensibles dans le stockage sécurisé du système. Android fournit le Keystore, iOS le Trousseau (Keychain) et la Secure Enclave : des espaces où les clés cryptographiques sont générées et utilisées sans jamais pouvoir être extraites, souvent dans une puce dédiée. Un jeton de rafraîchissement ou une clé de chiffrement de la base locale y ont leur place. Les préférences ordinaires (SharedPreferences sur Android, UserDefaults sur iOS) et les fichiers en clair ne conviennent pas aux secrets.

Le minimum de données sur l'appareil, effacées à la déconnexion, et exclues des sauvegardes automatiques quand elles sont sensibles.

Les requêtes sur la base locale SQLite sont préparées, comme sur un serveur : le sujet de 2024 demandait d'écrire une insertion paramétrée dans une table de journal SQLite sur Android.

Authentification biométrique

Le sujet E6 SLAM 2024 (aujourd'hui E7) portait sur une application mobile de pompiers qui déverrouillait l'accès par biométrie, avec un journal des tentatives (corrigé du sujet 2024, dossier C). Trois idées à maîtriser.

L'application ne voit jamais l'empreinte ou le visage. Elle demande au système d'exploitation d'authentifier l'utilisateur, avec BiometricPrompt sur Android ou LocalAuthentication sur iOS. Le système compare l'empreinte au modèle enregistré dans la puce sécurisée de l'appareil et répond seulement « succès » ou « échec ».

téléphoneApplicationjeton chiffré stocké1. demandeBiometricPromptsystème Android2. compare l'empreintePuce sécuriséemodèle de l'empreinte, clé3. clé duKeystoredébloquée,déchiffrele jeton4. jeton, HTTPSAPI AppliFraisserveurL'empreinte ne quittejamais la puce ; le serveurne reçoit qu'un jeton.
La biométrie déverrouille une clé sur l'appareil ; ni l'application ni le serveur ne voient l'empreinte

La biométrie doit débloquer quelque chose. Un simple booléen « succès » dans le code de l'application se contourne sur un téléphone modifié. La bonne pratique lie l'authentification à une clé du Keystore créée avec l'exigence d'authentification de l'utilisateur : sans empreinte valide, la clé reste inutilisable, et le jeton qu'elle chiffre aussi. L'extrait Java simplifié suivant montre le principe :

java
BiometricPrompt.PromptInfo infos = new BiometricPrompt.PromptInfo.Builder()
        .setTitle("Déverrouiller AppliFrais")
        .setNegativeButtonText("Utiliser le mot de passe")       // une alternative est toujours proposée
        .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
        .build();

BiometricPrompt invite = new BiometricPrompt(this, executeur, new BiometricPrompt.AuthenticationCallback() {
    @Override
    public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult resultat) {
        Cipher dechiffreur = resultat.getCryptoObject().getCipher();  // débloqué par l'empreinte
        String jeton = dechiffrerJeton(dechiffreur);                  // jeton stocké chiffré sur l'appareil
        journal.ajouter(new EntreeLog("LoginBiometrie", "Succes", "OK"));
        ouvrirAccueil(jeton);
    }

    @Override
    public void onAuthenticationError(int code, @NonNull CharSequence message) {
        journal.ajouter(new EntreeLog("LoginBiometrie", "Echec", code + " - " + message));
    }
});

invite.authenticate(infos, new BiometricPrompt.CryptoObject(dechiffreurInitialise));

La biométrie est une donnée sensible au sens du RGPD. Les données biométriques traitées pour identifier une personne de façon unique font partie des catégories particulières de données (article 9), dont le traitement est interdit par principe, sauf exceptions encadrées. Quand le modèle reste sur l'appareil, sous le seul contrôle de l'utilisateur, et que l'application n'en reçoit qu'un résultat, le risque est bien moindre qu'avec une base centrale d'empreintes. Mais dans un contexte professionnel, l'employeur doit justifier le recours à la biométrie, proposer une alternative (code, mot de passe), informer les personnes et mener une analyse d'impact si le traitement présente un risque élevé. La CNIL a publié un règlement type sur la biométrie sur les lieux de travail.

Communications de l'application mobile

  • HTTPS obligatoire. Depuis Android 9, une application qui cible une version récente du système refuse par défaut le trafic en clair ; la configuration de sécurité réseau (network_security_config.xml) ne doit pas le réautoriser. Sur iOS, App Transport Security impose HTTPS par défaut.
  • Ne jamais désactiver la vérification des certificats, même « temporairement pour les tests » : ce code finit en production.
  • L'épinglage de certificat (certificate pinning), qui n'accepte que le certificat ou la clé attendus du serveur, protège contre une autorité de certification compromise, mais complique le renouvellement des certificats : à réserver aux applications sensibles, avec une clé de secours.
  • Le serveur reste le juge. Tous les contrôles d'accès faits dans l'application mobile doivent être refaits par l'API : une application modifiée ou un script peuvent appeler l'API directement.

Exercice 1 sur 4 Classer dans l'API Top 10 Réussi À revoir

Donne le code de l'OWASP API Security Top 10 (2023) correspondant à chaque constat.

  1. GET /api/visiteurs/12 renvoie le profil complet avec mdp_hash et iban.
  2. GET /api/fiches?limite=999999 renvoie toute la table.
  3. DELETE /api/utilisateurs/12 fonctionne avec un jeton de visiteur.
  4. /api/v1/fiches, remplacée par la v2, renvoie toujours les fiches sans authentification.
  5. Un visiteur lit la fiche 1188 qui ne lui appartient pas.
Voir la réponseMasquer la réponse de l'exercice 1
  1. API3:2023 Broken Object Property Level Authorization : des propriétés sensibles sont exposées.
  2. API4:2023 Unrestricted Resource Consumption : pas de plafond de pagination.
  3. API5:2023 Broken Function Level Authorization : une fonction d'administration accessible à un rôle qui n'y a pas droit.
  4. API9:2023 Improper Inventory Management : une ancienne version oubliée.
  5. API1:2023 Broken Object Level Authorization (BOLA).

Tu avais trouvé ?

Exercice 2 sur 4 Sécuriser une route DELETE Réussi À revoir

Voici la route de suppression d'une ligne de frais :

js
router.delete('/api/lignes/:id', async (req, res) => {
  await pool.execute('DELETE FROM ligne_frais WHERE id = ?', [req.params.id]);
  res.status(204).end();
});
  1. Liste les contrôles manquants.
  2. Réécris la route : seul le visiteur propriétaire peut supprimer une ligne, et seulement si la fiche est en cours de saisie (CR).
  3. Quel code renvoyer si la ligne n'existe pas ou ne peut pas être supprimée ?
Voir la réponseMasquer la réponse de l'exercice 2
  1. Aucune authentification ; aucun contrôle du propriétaire de la ligne ; aucun contrôle de l'état de la fiche (on pourrait supprimer une ligne d'une fiche validée ou remboursée) ; aucune journalisation de la suppression.
  2. Les conditions vont dans la requête elle-même :
js
router.delete('/api/lignes/:id', authentifier, exigerRole('visiteur'), async (req, res) => {
  const [resultat] = await pool.execute(
    `DELETE l FROM ligne_frais l
     JOIN fiche_frais f ON f.id = l.id_fiche
     WHERE l.id = ? AND f.id_visiteur = ? AND f.etat = 'CR'`,
    [req.params.id, req.utilisateur.id]
  );
  if (resultat.affectedRows === 0) return res.status(404).json({ erreur: 'Ligne introuvable ou non modifiable' });
  journaliserSecurite('suppression_ligne', req, { ressource: `ligne:${req.params.id}` });
  res.status(204).end();
});
  1. 404 (ou 409 si l'on veut distinguer une fiche qui n'est plus modifiable, à condition que la ligne appartienne bien au demandeur). Sans jeton valide, le middleware renvoie 401 ; avec un autre rôle, 403.

Tu avais trouvé ?

Exercice 3 sur 4 Application mobile Réussi À revoir

Un développeur prépare l'application mobile d'AppliFrais. Pour chaque choix, dis s'il est acceptable et justifie.

  1. La clé d'API du service de cartographie (payant) est écrite dans une constante du code Java.
  2. Le jeton de rafraîchissement est chiffré avec une clé du Keystore qui exige une authentification biométrique.
  3. Après une authentification biométrique réussie, l'application met estConnecte = true et appelle l'API sans jeton.
  4. Pour les tests, le développeur accepte tous les certificats TLS ; il compte retirer ce code avant la publication.
Voir la réponseMasquer la réponse de l'exercice 3
  1. Non : l'APK se décompile, la clé sera extraite et utilisée aux frais de l'entreprise. L'application appelle le serveur d'AppliFrais, qui appelle le service de cartographie avec la clé.
  2. Oui : c'est la bonne pratique. Le jeton est inutilisable sans l'empreinte, même si les fichiers de l'application sont copiés.
  3. Non : le booléen se contourne sur un téléphone modifié, et l'API doit authentifier chaque appel. La biométrie doit débloquer un secret (le jeton), qui est ensuite présenté à l'API.
  4. Non : ce code a toutes les chances d'arriver en production et rend l'application vulnérable à l'interception (M5). Pour les tests, on installe sur l'appareil de test le certificat de l'autorité de développement, sans toucher à la vérification.

Tu avais trouvé ?

Exercice 4 sur 4 Biométrie et RGPD Réussi À revoir

Le laboratoire GSB veut que les visiteurs déverrouillent l'application mobile par reconnaissance faciale.

  1. Pourquoi cette donnée demande-t-elle des précautions particulières ?
  2. Quelle différence de risque y a-t-il entre utiliser la reconnaissance faciale du téléphone (BiometricPrompt) et envoyer une photo du visage au serveur pour comparaison ?
  3. Quelles obligations l'employeur doit-il respecter ?
Voir la réponseMasquer la réponse de l'exercice 4
  1. Une donnée biométrique traitée pour identifier une personne de façon unique est une catégorie particulière de données au sens de l'article 9 du RGPD : son traitement est interdit par principe, sauf exception. Contrairement à un mot de passe, elle ne se change pas si elle fuit.
  2. Avec BiometricPrompt, le modèle du visage reste dans la puce sécurisée du téléphone, sous le contrôle de l'utilisateur ; l'application et le serveur ne reçoivent qu'un résultat. Envoyer une photo crée une base centrale de visages : une fuite toucherait tous les visiteurs, de façon irréversible. Le premier choix est bien moins risqué.
  3. Justifier la nécessité et la proportionnalité du dispositif, proposer une alternative (mot de passe ou code) sans pénaliser ceux qui refusent, informer les salariés, inscrire le traitement au registre, mener une analyse d'impact (AIPD) si le traitement présente un risque élevé, et respecter le cadre fixé par la CNIL pour la biométrie au travail.

Tu avais trouvé ?

Chapitre terminé !

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

chapitres
–
de série
–
du cours
–
Chapitre suivant : Tests de sécurité : revue de code, SAST, DAST et SCARevoir le sommaire du cours