web-dev-qa-db-fra.com

Comment empêchez-vous les attaques imbriquées sur le serveur GraphQL/Apollo?

Comment empêcher une attaque imbriquée contre un serveur Apollo avec une requête telle que:

{
  authors {
    firstName
    posts {
      title
      author {
        firstName
        posts{
          title
          author {
            firstName
            posts {
              title
              [n author]
                [n post]
            }
          }
        }
      }
    }
  }
}

En d'autres termes, comment pouvez-vous limiter le nombre de récurrences soumises dans une requête? Cela pourrait être une vulnérabilité potentielle du serveur. 

43
jboothe

Au moment de la rédaction de ce document, GraphQL-JS ou Apollo Server ne comportait pas de fonction intégrée permettant de résoudre ce problème, mais cette solution devrait certainement avoir une solution simple à mesure que GraphQL gagne en popularité. Cette préoccupation peut être résolue avec plusieurs approches à plusieurs niveaux de la pile, et doit également toujours être combinée à une limitation de débit, de sorte que les personnes ne puissent pas envoyer trop de requêtes à votre serveur (c'est un problème potentiel avec REST ainsi que).

Je vais simplement énumérer toutes les différentes méthodes auxquelles je peux penser, et je vais essayer de garder cette réponse à jour au fur et à mesure que ces solutions sont implémentées dans divers serveurs GraphQL. Certains d'entre eux sont assez simples, et d'autres sont plus complexes.

  1. Query validation: sur chaque serveur GraphQL, la première étape de l’exécution d’une requête est validation: le serveur essaie de déterminer s’il existe des erreurs sérieuses dans la requête afin de évitez d'utiliser les ressources réelles du serveur si nous pouvons constater qu'il y a une erreur de syntaxe ou un argument invalide au début. GraphQL-JS est livré avec une sélection de règles par défaut qui suivent un format assez similaire à ESLint. Tout comme il existe une règle pour détecter des cycles infinis en fragments , vous pouvez écrire une règle de validation pour détecter les requêtes comportant trop d'imbrication et les rejeter à l'étape de validation.
  2. Query timeout: S'il n'est pas possible de détecter qu'une requête consomme trop de ressources en ressources (peut-être même des requêtes superficielles peuvent être très coûteuses!), Nous pouvons simplement ajouter un délai à l'exécution de la requête. Cela présente quelques avantages: (1) c'est une limite difficile sur laquelle il n'est pas trop difficile de raisonner, et (2) cela aidera également dans les situations où l'un des backends prend trop de temps à répondre. Dans de nombreux cas, un utilisateur de votre application préférerait un champ manquant à plus de 10 secondes d'attente pour obtenir une réponse.
  3. Query Whitelisting: C'est probablement la méthode la plus impliquée, mais vous pouvez compiler une liste de requêtes autorisées à l'avance et vérifier toutes les requêtes entrantes par rapport à cette liste. Si vos requêtes sont totalement statiques (vous n'effectuez aucune génération de requête dynamique sur le client avec quelque chose comme Relay), c'est l'approche la plus fiable. Vous pouvez utiliser un outil automatisé pour extraire les chaînes de requête de vos applications lorsqu'elles sont déployées. Ainsi, lors du développement, vous écrivez toutes les requêtes que vous souhaitez, mais en production, seules celles que vous souhaitez laisser passer. Un autre avantage de cette approche est que vous pouvez ignorer entièrement la validation des requêtes, car vous savez que toutes les requêtes possibles sont déjà valides. Pour plus d’avantages des requêtes statiques et des listes blanches, lisez ce message: https://dev-blog.apollodata.com/5-benefits-of-static-graphql-queries-b7fa90b0b69a
  4. Limitation du coût de la requête: (ajouté dans une modification) De la même manière que les délais de requête, vous pouvez affecter un coût à différentes opérations lors de l'exécution de la requête, par exemple une requête de base de données, et limiter le coût total que le client peut utiliser par requête. . Cela peut être associé à la limitation du parallélisme maximal d'une requête unique, de sorte que vous puissiez empêcher le client d'envoyer quelque chose qui initie des milliers de demandes parallèles à votre serveur.

(1) et (2) en particulier sont probablement quelque chose que tout serveur GraphQL devrait avoir par défaut, d’autant plus que de nombreux nouveaux développeurs risquent de ne pas être conscients de ces préoccupations. (3) ne fonctionnera que pour certains types d’applications, mais pourrait constituer un bon choix lorsque les performances ou les exigences de sécurité sont très strictes.

46
stubailo

Pour compléter le point 4 de la réponse de stubailo, voici quelques implémentations de Node.js qui imposent bornes de coût et de profondeur aux documents GraphQL entrants.

Ce sont des règles personnalisées qui complètent la phase de validation.

9
Andy Carlson

Pour limiter les coûts de la requête, vous pouvez utiliser graphql-cost-analysis

Il s'agit d'une règle de validation qui analyse la requête avant de l'exécuter. Dans votre serveur GraphQL, il vous suffit d’affecter une configuration de coût à chaque champ de votre mappe de types de schéma de votre choix.

2
Schrax

Une variante de la liste blanche des requêtes est query signature

Pendant le processus de construction, chaque requête est signée de manière cryptographique à l'aide d'un secret partagé avec le serveur mais non associé au client. Ensuite, au moment de l'exécution, le serveur peut valider qu'une requête est authentique. 

L'avantage par rapport à la liste blanche est que l'écriture de requêtes dans le client ne nécessite aucune modification du serveur. Cela est particulièrement utile si plusieurs clients accèdent au même serveur (applications Web, de bureau et mobiles, par exemple).

2
Tamlyn

Ne manquez pas graphql-rate-limit ???? une directive GraphQL pour ajouter une limitation de débit simple mais granulaire à vos requêtes ou mutations.

0
Carlos Rufo