Sécurité GraphQL

GraphQL offre une flexibilité puissante mais introduit des vecteurs d'attaque uniques : attaques de query complexity, abus d'introspection, contournement d'autorisation et divulgation d'informations.

Vulnérabilités courantes dans GraphQL

1. Attaques de Query Depth / Complexity

Les requêtes profondément imbriquées peuvent provoquer un DoS en consommant des ressources excessives :

      query MaliciousQuery {
      user(id: "1") {
      posts {
      comments {
      author {
      posts {
      comments {
      author {
      posts {
      # ... imbriqué à l'infini
      }
      }
      }
      }
      }
      }
      }
      }
      }
      

2. Abus d'introspection

L'introspection activée en production expose le schéma complet, facilitant la reconnaissance :

      query IntrospectionQuery {
      __schema {
      types {
      name
      fields {
      name
      type {
      name
      }
      }
      }
      }
      }
      

3. Autorisation défaillante

L'autorisation doit avoir lieu dans les resolvers, et pas seulement dans les requêtes de premier niveau. L'IDOR est fréquent lorsque l'autorisation n'est pas vérifiée sur les champs imbriqués.

4. Attaques par injection

GraphQL n'est pas à l'abri des SQLi ou NoSQLi si les entrées ne sont pas assainies dans les resolvers.

Protections essentielles

Limitation de Query Depth

      // Exemple avec Apollo Server
      import depthLimit from 'graphql-depth-limit';
      const server = new ApolloServer({
      typeDefs,
      resolvers,
      validationRules: [depthLimit(5)]  // Profondeur max 5
      });
      

Analyse de Query Complexity

      import { createComplexityLimitRule } from 'graphql-validation-complexity';
      const server = new ApolloServer({
      validationRules: [
      createComplexityLimitRule(1000, {
      scalarCost: 1,
      objectCost: 5,
      listFactor: 10
      })
      ]
      });
      

Rate Limiting

  • Basé sur les requêtes : Limiter les requêtes par minute et par utilisateur
  • Basé sur la complexity : Budget de complexity points
  • Analyse de coût : Attribuer un coût à chaque champ

Autorisation dans GraphQL

Autorisation au niveau des champs

      const resolvers = {
      Query: {
      user: async (parent, { id }, context) => {
      // Autorisation au niveau de la query
      if (!context.user) {
      throw new AuthenticationError('Not authenticated');
      }
      return getUserById(id);
      }
      },
      User: {
      email: (user, args, context) => {
      // Autorisation au niveau du champ
      if (context.user.id !== user.id && !context.user.isAdmin) {
      return null;  // Masquer l'email aux autres utilisateurs
      }
      return user.email;
      },
      ssn: (user, args, context) => {
      // Champ extrêmement sensible
      if (!context.user.isAdmin) {
      throw new ForbiddenError('Admin only');
      }
      return user.ssn;
      }
      }
      };
      

Autorisation basée sur les directives

      type User @auth(requires: AUTHENTICATED) {
      id: ID!
      username: String!
      email: String! @auth(requires: OWNER_OR_ADMIN)
      ssn: String! @auth(requires: ADMIN)
      }
      

Protection de l'introspection

      const server = new ApolloServer({
      typeDefs,
      resolvers,
      introspection: process.env.NODE_ENV !== 'production',
      // Ou contrôler par rôle
      plugins: [{
      requestDidStart() {
      return {
      didResolveOperation({ request, context }) {
      if (request.operationName === 'IntrospectionQuery'
      && !context.user?.isAdmin) {
      throw new ForbiddenError('Introspection disabled');
      }
      }
      }
      }
      }]
      });
      

Validation des entrées

  • Validation de schéma : GraphQL valide les types automatiquement
  • Custom scalars : Email, URL, DateTime avec validation
  • Assainissement des entrées : Assainir les entrées avant de les utiliser dans les resolvers
  • Requêtes paramétrées : Utiliser des prepared statements dans la base de données

Batching et DataLoader

Prévenir le problème N+1, qui peut être exploité pour un DoS :

      import DataLoader from 'dataloader';
      const userLoader = new DataLoader(async (userIds) => {
      const users = await getUsersByIds(userIds);
      return userIds.map(id => users.find(u => u.id === id));
      });
      const resolvers = {
      Post: {
      author: (post, args, { userLoader }) => {
      return userLoader.load(post.authorId);  // En lot !
      }
      }
      };
      

Surveillance et journalisation

  • Journalisation des requêtes : Journaliser toutes les requêtes avec leurs metadata (utilisateur, IP, heure)
  • Suivi des erreurs : Sentry, Datadog pour les exceptions
  • Surveillance des performances : Apollo Studio, GraphQL Hive
  • Détection d'anomalies : Alerter sur les requêtes suspectes (trop complexes, etc.)

Outils de sécurité

  • GraphQL Armor : Suite de middleware de sécurité
  • graphql-shield : Couche de permissions avec règles
  • InQL : Extension Burp Suite pour le pentest GraphQL
  • BatchQL : Outil de security testing pour GraphQL
  • graphql-cop : Outil d'audit de sécurité

OWASP GraphQL Top 10

  1. Broken Object Level Authorization
  2. Broken Authentication
  3. Excessive Data Exposure
  4. Resource Exhaustion
  5. Broken Function Level Authorization
  6. Mass Assignment
  7. Security Misconfiguration
  8. Injection
  9. Improper Assets Management
  10. Insufficient Logging & Monitoring

Pratiques de développement sécurisé

  • Implémenter l'autorisation dans TOUS les resolvers
  • Désactiver l'introspection en production
  • Limiter la query depth et la complexity
  • Rate limiting agressif
  • Utiliser DataLoader pour prévenir le N+1
  • Assainir les entrées avant traitement
  • Mettre en place une journalisation exhaustive
  • Audits de sécurité et pentests réguliers

Recommandations finales

GraphQL exige un changement de mentalité en matière de sécurité par rapport à REST. La flexibilité du client requiert des défenses robustes côté serveur. Implémentez le depth limiting, l'analyse de complexity et l'autorisation au niveau des champs dès le départ. Utilisez des outils de surveillance pour détecter les schémas d'abus. Testez régulièrement avec des outils spécialisés comme InQL et BatchQL.