Ressources · 37

Sécuriser une API GraphQL : droits, coût des requêtes et preuves

Contrôler chaque objet et champ, borner le travail demandé au serveur et tester les refus.

· 8 min

Salle cyber fictive sans personnes, avec cubes de verre reliés par des nœuds, graphe abstrait, coffret bordeaux et appareil réseau sur une table sombre

Ce que ce guide permet

  • Cartographier le schéma exposé
  • Éprouver les droits
  • Borner le coût
  • Vérifier les refus

Contrôle express

  • Une personne peut-elle lire l’objet d’un autre compte ?
  • Un champ sensible suit-il la même décision que son parent ?
  • Que coûtent profondeur, alias et pagination combinés ?
  • Quels détails fuitent dans les erreurs ?
  • Qui voit les refus et les abus ?

Méthode pas à pas

  1. 01

    Inventorier les opérations

    Lister types, champs, mutations, rôles, propriétaires des objets et données sensibles. Inclure les requêtes des clients actuels et les chemins rarement appelés.

    Livrable : matrice opération, rôle, objet et champ.

  2. 02

    Tester chaque décision de droit

    Créer des comptes de test distincts et échanger les identifiants d’objet, y compris dans les objets imbriqués. Le résolveur doit appliquer l’autorisation avant toute réponse.

    Livrable : cas autorisés et refusés reproductibles.

  3. 03

    Borner le travail du serveur

    Mesurer le coût réel des champs et collections. Tester profondeur, largeur, alias, pagination, lotissement et délais ensemble, puis fixer des limites adaptées.

    Livrable : politique de coût et jeu de requêtes adverses.

  4. 04

    Limiter l’exposition inutile

    Décider de l’introspection selon le contexte, masquer les détails internes des erreurs et éviter les champs qui révèlent plus que le besoin client.

    Livrable : configuration publique et réponses d’erreur revues.

  5. 05

    Observer sans collecter trop

    Journaliser identité technique, opération, coût, refus et corrélation utiles, en écartant secrets et arguments sensibles. Définir seuils et propriétaire des alertes.

    Livrable : tableau de bord et procédure d’enquête.

  6. 06

    Rejouer après changement

    Après ajout de champ, rôle ou client, rejouer les cas de droits et de charge. Vérifier aussi les anciennes requêtes qui traversent le nouveau graphe.

    Livrable : résultat de régression et décision de publication.

Indicateurs de pilotage

IndicateurCe qu’il mesurePremière action
DroitsCas objet et champ refusés comme prévuCorriger le résolveur fautif
CoûtRequêtes lourdes contenues par la politiqueAjuster champs et limites
ErreursRéponses publiques sans information interneRéduire les détails exposés
RégressionOpérations clients rejouées après évolutionBloquer une rupture non prévue

Erreurs fréquentes

  • Faire confiance au seul accès à l’endpoint
  • Limiter uniquement la profondeur sans examiner la largeur
  • Oublier l’autorisation des champs imbriqués
  • Journaliser les arguments contenant des données sensibles

Questions fréquentes

GraphQL remplace-t-il le contrôle de droits REST ?

Non. Chaque accès aux objets et champs doit toujours être autorisé selon l’identité et le contexte.

Désactiver l’introspection suffit-il ?

Non. Cela peut réduire l’exposition du schéma, mais ne corrige ni droits ni coût des requêtes.

Une limite de profondeur suffit-elle ?

Non. Alias, largeur, listes et coûts différents selon les champs doivent aussi être pris en compte.

GraphQL : erreurs, listes et données partielles

Comment lire une réponse GraphQL quand une partie des données échoue ? Commencer par la phase du traitement, puis suivre les contraintes Non-Null jusqu’à la première position qui accepte null. Un écran qui affiche seulement « succès » parce que le transport a répondu risque de masquer une fiche incomplète. Ce dossier relie le schéma, le résultat JSON et la décision d’interface dans un scénario fictif de consultation de preuves.

Référence : édition publiée de septembre 2025, relue le 23 octobre 2026. Les huit règles ci-dessous sont une sélection éditoriale CIE ; elles ne constituent pas une certification de conformité. Les scénarios et jeux de données sont entièrement fictifs.

GraphQL Specification · September 2025 · Published edition · September 2025

Huit repères pour interpréter le résultat

Huit repères pour interpréter le résultat
RepèreConséquence pour le clientSource
Request error resultUne erreur qui empêche l’exécution produit errors et aucune entrée data. Une variable invalide appartient à ce parcours, même si le document est syntaxiquement valide.GraphQL 2025
Execution resultAprès exécution, data est présente. Sa valeur peut être un objet partiel ou null. Sans erreur levée, errors doit être absente, et non une liste vide.GraphQL 2025
errors[].messageChaque erreur comporte un message destiné au développeur. Pour le visiteur, prévoir un libellé exploitable sans détails internes ni identifiants sensibles.GraphQL 2025
errors[].locationsQuand un point du document peut être associé à l’erreur, les positions devraient préciser line et column, à partir de 1. Elles ne constituent pas un chemin dans les données.GraphQL 2025
errors[].pathLe chemin emploie les noms de réponse, donc les alias, et des indices entiers à partir de 0. Il conserve la position en cause même si un parent est ensuite remplacé par null.GraphQL 2025
List / Non-NullDistinguer la nullabilité de la liste de celle de ses éléments. Un élément non nul en erreur annule la liste ; une liste non nulle propage encore l’erreur vers son parent.GraphQL 2025
extensionsLes extensions de réponse et d’erreur sont des objets de métadonnées définies par l’implémentation. Un champ code dans extensions n’est pas un catalogue universel de codes GraphQL.GraphQL 2025
GraphQL / HTTPLa spécification GraphQL ne fixe pas le transport. Le projet GraphQL over HTTP est présenté ici comme un brouillon de travail ; sa négociation de type média doit être testée séparément.HTTP draft

Identifier la phase avant de choisir le message

Identifier la phase avant de choisir le message
SituationForme attendueAction dans le parcours
Requête refusée avant exécutionerrors non vide ; data absenteCorriger la requête ou ses variables. Conserver une erreur utile sans relancer automatiquement une demande inchangée.
Exécution sans erreur levéedata présente ; errors absenteAfficher les données obtenues. Une valeur null autorisée par le schéma peut être normale et ne suffit pas à diagnostiquer une panne.
Exécution avec résultat partieldata objet ; errors non videDistinguer les champs conservés des positions en erreur. Empêcher une validation qui dépend d’une preuve indisponible.
Propagation jusqu’à la racinedata: null ; errors non videAfficher un état indisponible cohérent pour l’opération. Ne pas recycler silencieusement une réponse d’un autre dossier.

Simulateur de nullabilité GraphQL

Le modèle fixe une requête query avec affaire comme alias du champ dossier. Le dossier contient reference: String! et preuves, une liste de trois String. statut: String! est un champ frère à la racine. Chaque élément vaut A-01, A-02 ou A-03 avant la panne injectée. Choisissez le type de preuves, la nullabilité de dossier et l’élément en erreur.

query Demo {
  affaire: dossier { reference preuves }
  statut
}

Simulation locale d’une seule erreur de résolution d’élément, sur ce schéma fixe. Aucun appel API, analyseur de requêtes, test de droits, calcul de coût ou verdict HTTP. Les types imbriqués, abonnements et livraisons incrémentales ne sont pas modélisés. Le JSON sert à observer la propagation ; il ne contient aucune donnée d’enquête réelle.

Quatre types de liste, deux contraintes distinctes

Quatre types de liste, deux contraintes distinctes
Type de preuvesListe null autoriséeÉlément null autoriséPanne d’un élément
[String]OuiOuiUn élément devient null.
[String!]OuiNonLa liste devient null.
[String]!NonOuiUn élément devient null ; la liste reste présente.
[String!]!NonNonPropagation vers dossier, puis selon son type vers la racine.

Huit résultats de référence, consultables sans JavaScript

G01 · [String] · affaire: Dossier

Seul l’élément d’indice 1 devient null ; les deux autres éléments et le dossier restent lisibles.

{
    "data": {
        "affaire": {
            "reference": "DEMO-01",
            "preuves": [
                "A-01",
                null,
                "A-03"
            ]
        },
        "statut": "fictif"
    },
    "errors": [
        {
            "message": "Illustrative item resolution failure",
            "path": [
                "affaire",
                "preuves",
                1
            ]
        }
    ]
}

Contrôle de recette : Vérifier le JSON reçu et conserver errors[0].path = ["affaire", "preuves", 1], même lorsque affaire disparaît derrière null.

G02 · [String!] · affaire: Dossier

La liste preuves devient null ; la référence du dossier et le statut restent lisibles.

{
    "data": {
        "affaire": {
            "reference": "DEMO-01",
            "preuves": null
        },
        "statut": "fictif"
    },
    "errors": [
        {
            "message": "Illustrative item resolution failure",
            "path": [
                "affaire",
                "preuves",
                1
            ]
        }
    ]
}

Contrôle de recette : Vérifier le JSON reçu et conserver errors[0].path = ["affaire", "preuves", 1], même lorsque affaire disparaît derrière null.

G03 · [String]! · affaire: Dossier

Seul l’élément d’indice 1 devient null ; les deux autres éléments et le dossier restent lisibles.

{
    "data": {
        "affaire": {
            "reference": "DEMO-01",
            "preuves": [
                "A-01",
                null,
                "A-03"
            ]
        },
        "statut": "fictif"
    },
    "errors": [
        {
            "message": "Illustrative item resolution failure",
            "path": [
                "affaire",
                "preuves",
                1
            ]
        }
    ]
}

Contrôle de recette : Vérifier le JSON reçu et conserver errors[0].path = ["affaire", "preuves", 1], même lorsque affaire disparaît derrière null.

G04 · [String!]! · affaire: Dossier

Le dossier affaire devient null ; le champ frère statut reste disponible.

{
    "data": {
        "affaire": null,
        "statut": "fictif"
    },
    "errors": [
        {
            "message": "Illustrative item resolution failure",
            "path": [
                "affaire",
                "preuves",
                1
            ]
        }
    ]
}

Contrôle de recette : Vérifier le JSON reçu et conserver errors[0].path = ["affaire", "preuves", 1], même lorsque affaire disparaît derrière null.

G05 · [String] · affaire: Dossier!

Seul l’élément d’indice 1 devient null ; les deux autres éléments et le dossier restent lisibles.

{
    "data": {
        "affaire": {
            "reference": "DEMO-01",
            "preuves": [
                "A-01",
                null,
                "A-03"
            ]
        },
        "statut": "fictif"
    },
    "errors": [
        {
            "message": "Illustrative item resolution failure",
            "path": [
                "affaire",
                "preuves",
                1
            ]
        }
    ]
}

Contrôle de recette : Vérifier le JSON reçu et conserver errors[0].path = ["affaire", "preuves", 1], même lorsque affaire disparaît derrière null.

G06 · [String!] · affaire: Dossier!

La liste preuves devient null ; la référence du dossier et le statut restent lisibles.

{
    "data": {
        "affaire": {
            "reference": "DEMO-01",
            "preuves": null
        },
        "statut": "fictif"
    },
    "errors": [
        {
            "message": "Illustrative item resolution failure",
            "path": [
                "affaire",
                "preuves",
                1
            ]
        }
    ]
}

Contrôle de recette : Vérifier le JSON reçu et conserver errors[0].path = ["affaire", "preuves", 1], même lorsque affaire disparaît derrière null.

G07 · [String]! · affaire: Dossier!

Seul l’élément d’indice 1 devient null ; les deux autres éléments et le dossier restent lisibles.

{
    "data": {
        "affaire": {
            "reference": "DEMO-01",
            "preuves": [
                "A-01",
                null,
                "A-03"
            ]
        },
        "statut": "fictif"
    },
    "errors": [
        {
            "message": "Illustrative item resolution failure",
            "path": [
                "affaire",
                "preuves",
                1
            ]
        }
    ]
}

Contrôle de recette : Vérifier le JSON reçu et conserver errors[0].path = ["affaire", "preuves", 1], même lorsque affaire disparaît derrière null.

G08 · [String!]! · affaire: Dossier!

data devient null : la chaîne des positions non nulles atteint la racine.

{
    "data": null,
    "errors": [
        {
            "message": "Illustrative item resolution failure",
            "path": [
                "affaire",
                "preuves",
                1
            ]
        }
    ]
}

Contrôle de recette : Vérifier le JSON reçu et conserver errors[0].path = ["affaire", "preuves", 1], même lorsque affaire disparaît derrière null.

Six vérifications métier pour une API GraphQL de consultation

Six vérifications métier pour une API GraphQL de consultation
Scénario CIEPreuve à recueillirCritère de décision
G-ACC · Objet d’un autre tenantDeux identités de recette et un même identifiant de dossier, sans données de production.Le service applique les droits au dossier, aux relations et aux champs ; posséder un ID ne donne aucun accès.
G-ALIAS · Deux alias, même objetRésultats de deux sélections aliasées et traces corrélées sur un environnement autorisé.Les alias ne changent ni le périmètre d’accès ni la politique de coût. Le client relie chaque erreur au nom de réponse reçu.
G-CACHE · Changement de compteCapture du parcours de déconnexion puis connexion avec un autre compte de test.Les données mises en cache ne traversent pas le changement d’identité. Une réponse partielle n’entretient pas une ancienne preuve comme si elle venait d’être relue.
G-COUT · Volume et profondeurMesures de recette sur cardinalité, profondeur, temps et quotas définis pour le service.La profondeur seule ne suffit pas : encadrer aussi les listes, les champs coûteux et les opérations regroupées. Aucun seuil universel n’est inventé.
G-ERREUR · Message publicExemples des messages présentés au visiteur et des traces réservées aux opérateurs.Pas de pile technique, secret, nom de table ou preuve sensible dans le message exposé. Un identifiant de corrélation non sensible aide le support.
G-SXO · Action bloquée par une preuve manquanteParcours clavier et lecteur d’écran, avec un champ indispensable en erreur.L’état partiel est annoncé, les données conservées restent lisibles et la validation qui dépend du champ est expliquée puis empêchée.

OWASP · GraphQL Cheat Sheet · Living guidance consulted 2026-10-23

Une réponse partielle ne démontre ni une fuite ni une protection correcte. Seuls des tests d’autorisation côté service permettent de vérifier l’accès. Relier chaque recette au schéma déployé, à sa version et aux observations du client : le contrôle de données, la résilience de l’interface et le transport se vérifient ensemble, avec des critères séparés.

Provenance, édition et licence

GraphQL Specification · September 2025 · reviewed 2026-10-23. CIE: original explanations, fictional scenarios and limited educational model.

GraphQL HTML SHA-256: 39fb3aeffd9b5acebb0830ddc6dcc243c1280a9f0e977897ae59c5cc165511d6

HTTP draft HTML SHA-256: 90a686f8251073b05e105697c6028288520d0c0fa6442924c0d1751cad3f14ac

GraphQL over HTTP · Current Working Draft · Working draft consulted 2026-10-23 · may change

GraphQL · Copyright and Licensing · Open Web Foundation Agreement 1.0

Références officielles

Date de consultation des références : . La méthode et la fiche de travail proposent des contrôles à adapter à votre contexte ; elles ne constituent pas une certification.