Skip to main content
BlogCalculDéfendez votre serveur GraphQL contre la consommation excessive de ressources

Défendez votre serveur GraphQL contre la consommation excessive de ressources

Illustration d'un graphique à barres avec de nombreux points de données créant une apparence de vague avec du texte : Défendez votre serveur GraphQL contre la consommation excessive de ressources.

GraphQL offre la possibilité de demander exactement les ressources et les attributs dont vous avez besoin, ni plus ni moins. Contrairement à REST, qui recherche souvent trop ou pas assez de données. Cette précision rend GraphQL très efficace. En outre, GraphQL propose des méthodes standard pour gérer la pagination, ce qui accroît encore sa flexibilité. Toutefois, des utilisateurs malveillants peuvent exploiter ces fonctionnalités GraphQL, ce qui peut présenter des risques importants pour la stabilité de votre serveur.

Dans ce blog, nous allons explorer comment la flexibilité de GraphQL peut se retourner contre vous. Nous nous concentrerons plus particulièrement sur une vulnérabilité mise en évidence par le Top 10 de la sécurité des API de l'OWASP: la consommation illimitée de ressources. Nous aborderons également des mesures pratiques pour protéger vos systèmes contre ce type d'abus.

La réponse de GraphQL à l'over-fetching et à l'under-fetching

GraphQL a été conçu en partie pour résoudre les problèmes de surcharge (over-fetching) que nous rencontrons habituellement avec REST. Prenons l'exemple d'une base de données de commerce électronique standard avec des utilisateurs, des produits et des commandes. Imaginons qu'il faille récupérer le montant total d'une commande à partir de l'identifiant de la commande. Avec REST, cette requête GET vers /orders/17 récupèrerait non seulement le montant en dollars, mais aussi les éléments suivants :

  • données de commande
  • informations de facturation
  • informations sur l'expédition
  • quantités de produits
  • taxe
  • état de la commande
  • ... et plus

C'est de la surenchère.

Avec REST, vous rencontrez également des problèmes de sous-récupération, lorsque vous devez rassembler des données associées provenant de plusieurs ressources. Imaginons que vous souhaitiez afficher un résumé, avec la date et le statut d'une commande, les noms et descriptions de tous les produits de la commande, ainsi que le nom et l'adresse électronique de l'utilisateur qui a passé la commande. Pour ce faire, vous devez envoyer plusieurs requêtes :

  • GET /commandes/17
  • GET /produits/1662527
  • GET /produits/9914188
  • GET /produits/3750021
  • GET /produits/7557109
  • GET /produits/6081142
  • GET /users/3314

GraphQL a été la réponse aux lacunes de REST. Vous pouvez demander exactement ce dont vous avez besoin, à travers les ressources associées. Pour réaliser ce qui précède, votre GraphQL pourrait ressembler à ceci :

query {
  order(id: "17") {
    date
    status
    products {
      name
      description
    }
    user {
      name
      email
    }
  }
}

C'est tellement simple et flexible ! Cependant, ce niveau de flexibilité signifie également qu'un utilisateur malveillant peut délibérément extraire trop de données.

Abuser de la flexibilité des requêtes

La syntaxe d'interrogation de GraphQL vous permet d'effectuer plusieurs requêtes au sein d'une même requête. Nous pourrions prendre la requête ci-dessus et faire quelque chose comme ceci :

query {
  Q1: order(id: "17") {
    date
    status
    products {
      name
      description
    }
    user {
      name
      email
    }
  }
  Q2: order(id: "17") {
    date
    status
    products {
      name
      description
    }
    user {
      name
      email
    }
  }
}

Cette fois, nous avons demandé les mêmes données que la requête précédente, mais nous les avons demandées deux fois. Naturellement, la réponse sera deux fois plus volumineuse que la requête unique. Mais que se passe-t-il si nous répétons la requête 100 fois en une seule fois ?

query {
  Q00: order(id: "17") {    …  }  Q01: order(id: "17") {    …  }  …  Q99: order(id: "17") {    …  }
}

C'est ainsi que nous avons créé une requête qui produirait une réponse 100 fois plus volumineuse que notre requête initiale. L'image suivante de cheeseburgers illustre bien cette attaque. Les pains à hamburger constituent la requête unique ; mais entre les pains, il peut y avoir des centaines de galettes de bœuf !

Requête farcie

Si un utilisateur malveillant devait abuser de cette souplesse d'interrogation, la consommation excessive de ressources pourrait étouffer votre serveur.

Abuser des fonctions de pagination

GraphQL propose également des méthodes standard pour gérer la pagination. Une approche courante est la pagination basée sur le décalage, dans laquelle l'appelant fournit le nombre d'éléments à récupérer (limite) et l'élément par lequel commencer (décalage).

Par exemple, une requête visant à obtenir des informations à partir d'une recherche de produit pourrait ressembler à ceci :

query {
  products(searchString: "shirt", limit: 8, offset: 0) {
    id
    name
    description
    sku
    category
    images {
      path
      alt_text
    }
    variations {
      name
      description
      cost
    }
  }
}

Et si nous ajustions les paramètres de pagination pour exécuter cette requête à la place ?

query {
  products(searchString: "shirt", limit: 800, offset: 0) {    …  }}

Dans une série d'exemples similaires que j'ai testés sur un site de commerce électronique, j'ai comparé les réponses que j'ai reçues :

$ du response*.json
680496  response_big.json
333649  response_small.json

On pourrait penser que le grand fichier est environ 100 fois plus volumineux que le petit puisque la limite a été fixée à 800 au lieu de 8. La recherche n'a probablement pas donné 800 résultats au total (peut-être seulement 16 à 20).

L'utilisation abusive de la pagination est un autre exemple de la manière dont quelqu'un peut manipuler les paramètres de requête GraphQL pour augmenter la charge de votre serveur. En exploitant cette fonctionnalité, les attaquants peuvent forcer votre serveur à traiter des requêtes beaucoup plus importantes que prévu, ce qui peut entraîner l'épuisement des ressources et un déni de service.

OWASP API4:2023 Consommation de ressources sans restriction

Ce que nous avons démontré relève du Top 10 de la sécurité des API de l'OWASP, en particulier API4:2023 Unrestricted Resource Consumption (consommation illimitée de ressources). Cette vulnérabilité survient lorsque les API ne limitent pas certains types d'interactions ou de requêtes, ce qui rend le serveur vulnérable aux attaques DoS et à d'autres perturbations opérationnelles.

Lorsqu'une API permet une interrogation ou une pagination excessive sans limites, elle peut entraîner une consommation de ressources en spirale. Cela peut à son tour entraîner une dégradation des performances, voire une panne complète du serveur. En l'absence de limites appropriées, ces attaques peuvent perturber le service. Vous risquez d'encourir des coûts opérationnels élevés, de voir vos clients se détourner de vous et de perdre des marchés.

Comment se protéger

Pour faire face à ces risques, vous devez réglementer correctement les demandes d'API. Mettez en place des contrôles qui limitent la quantité de données pouvant être demandées. Il s'agit notamment de fixer des limites à la taille et au nombre des requêtes, ainsi que de mettre en œuvre une limitation du débit et d'autres mesures de protection.

Voici quelques suggestions pratiques pour vous protéger :

  • Limitation de la taille des données demandées et renvoyées: En fixant des limites à la taille des données qui peuvent être demandées et renvoyées, vous pouvez éviter que des requêtes trop volumineuses ne submergent votre serveur.
  • Limites de pagination: Mettre en place une limite maximale pour la pagination, afin de s'assurer qu'un appelant ne tente pas de récupérer plus d'enregistrements qu'il n'est raisonnable de le faire.
  • Pare-feu d'application Web (WAF): Un WAF peut aider à détecter et à bloquer les activités malveillantes, en déjouant les attaques potentielles contre votre API GraphQL. Envisagez d'utiliser un WAF tel que Haltdos de Linode Marketplace pour ajouter cette couche de sécurité supplémentaire.
  • Outils de sécurité de l'API: Les outils capables de détecter et d'atténuer les activités malveillantes sont essentiels. Ils peuvent aider à identifier des schémas inhabituels susceptibles d'indiquer une attaque, ce qui vous permet de prendre des mesures avant qu'elle n'affecte votre système.

En prenant ces mesures, vous pouvez réduire considérablement le risque d'exploitation de votre serveur GraphQL en raison d'une consommation excessive de ressources. En veillant à ce que vos interactions avec l'API soient correctement régulées, vous contribuerez à maintenir la stabilité et les performances de votre serveur.

Pour plus d'informations sur la construction avec GraphQL, consultez GraphQL Apollo : Une introduction avec des exemples dans notre documentation.

Commentaires

Laissez un commentaire

Votre adresse électronique ne sera pas publiée. Les champs obligatoires sont marqués d'un *.