🚀 Nuova versione beta disponibile! Feedback o problemi? Contattaci

Protezione delle API GraphQL: Difendersi dagli Attacchi e Migliorare la Sicurezza

Codegrind TeamSep 03 2024

Le API GraphQL offrono un modo flessibile e potente per gestire le richieste di dati, ma, come tutte le API, possono essere vulnerabili a una serie di attacchi. La flessibilità di GraphQL, come la possibilità di comporre query complesse, può essere sfruttata da attori malintenzionati per sovraccaricare il server o accedere a dati sensibili. In questo articolo esploreremo le tecniche di protezione delle API GraphQL dagli attacchi comuni, migliorando la sicurezza e prevenendo abusi.

1. Limiti sulla Complessità delle Query

Uno dei rischi principali in GraphQL è la possibilità di inviare query molto complesse e profondamente nidificate, che possono sovraccaricare il server. Gli attaccanti possono creare query che, pur essendo valide, eseguono operazioni enormi e rallentano il sistema.

Soluzione: Analisi della Complessità delle Query

Imponi limiti sulla complessità delle query per prevenire attacchi DoS (Denial of Service) basati su query troppo complesse. Puoi usare librerie come graphql-query-complexity per analizzare e limitare la complessità delle query in GraphQL.

Implementazione con graphql-query-complexity

npm install graphql-query-complexity
const { getComplexity, simpleEstimator } = require("graphql-query-complexity");

const server = new ApolloServer({
  schema,
  plugins: [
    {
      requestDidStart: () => ({
        didResolveOperation({ request, document }) {
          const complexity = getComplexity({
            schema,
            query: document,
            variables: request.variables,
            estimators: [simpleEstimator({ defaultComplexity: 1 })],
          });

          if (complexity > 100) {
            throw new Error(
              `La complessità della query è troppo alta: ${complexity}`
            );
          }
        },
      }),
    },
  ],
});

Vantaggi:

  • Prevenzione degli abusi: Impedisce che una singola query troppo complessa sovraccarichi il server.
  • Controllo delle risorse: Assicura che le query siano entro limiti accettabili in termini di tempo e risorse.

2. Rate Limiting

Le API GraphQL possono essere vulnerabili a attacchi di forza bruta o bot che eseguono molte query in un breve periodo di tempo. Limitare il numero di richieste da un singolo client può aiutare a prevenire questi attacchi.

Soluzione: Implementare Rate Limiting

Utilizza un middleware di rate limiting come express-rate-limit per limitare il numero di richieste GraphQL che un client può inviare in un determinato periodo.

Implementazione con express-rate-limit

npm install express-rate-limit
const rateLimit = require("express-rate-limit");

// Configura il rate limit
const limiter = rateLimit({
  windowMs: 1 * 60 * 1000, // 1 minuto
  max: 100, // Limita ogni IP a 100 richieste al minuto
});

// Applica il rate limiter alle richieste GraphQL
app.use("/graphql", limiter);

Vantaggi:

  • Protezione dagli attacchi DoS: Limita la possibilità che un singolo client sovraccarichi il server.
  • Controllo del traffico: Riduce il rischio che bot o utenti malintenzionati abusino delle API.

3. Autenticazione e Autorizzazione

Assicurarsi che solo utenti autenticati e autorizzati possano accedere alle risorse sensibili è una delle componenti più importanti per la sicurezza delle API.

Soluzione: Usare JWT per l’Autenticazione

Un approccio comune è utilizzare JSON Web Token (JWT) per autenticare gli utenti e verificare che abbiano accesso alle risorse richieste. JWT permette al server di autenticare le richieste senza dover mantenere uno stato del login.

Implementazione di JWT

  1. Installazione delle dipendenze:
npm install jsonwebtoken
  1. Creare un Token JWT durante l’autenticazione:
const jwt = require("jsonwebtoken");

const generateToken = (user) => {
  return jwt.sign({ id: user.id, role: user.role }, "secret_key", {
    expiresIn: "1h",
  });
};
  1. Verificare il JWT nel contesto di Apollo Server:
const { ApolloServer } = require("apollo-server");
const jwt = require("jsonwebtoken");

const context = ({ req }) => {
  const token = req.headers.authorization || "";
  try {
    const user = jwt.verify(token, "secret_key");
    return { user };
  } catch (error) {
    throw new AuthenticationError("Token non valido");
  }
};

const server = new ApolloServer({
  typeDefs,
  resolvers,
  context,
});

Autorizzazione nei Risolutori

Una volta autenticato l’utente, puoi verificare il suo ruolo o le sue autorizzazioni nei risolutori per controllare l’accesso a risorse specifiche.

const resolvers = {
  Query: {
    sensitiveData: (parent, args, context) => {
      if (context.user.role !== "admin") {
        throw new ForbiddenError(
          "Non hai i permessi per accedere a questa risorsa"
        );
      }
      return getSensitiveData();
    },
  },
};

Vantaggi:

  • Autenticazione sicura: Solo gli utenti autenticati possono accedere alle API.
  • Controllo degli accessi: Assicura che gli utenti possano accedere solo alle risorse per cui sono autorizzati.

4. Query Whitelisting

Le API GraphQL consentono ai client di inviare query dinamiche, ma ciò può essere rischioso in quanto un attaccante potrebbe inviare query malformate o dannose. Un approccio per mitigare questo rischio è limitare le query ai soli client autorizzati.

Soluzione: Persisted Queries con Whitelisting

Le Persisted Queries permettono di predefinire un insieme di query approvate e di consentire ai client di eseguire solo quelle. Le query vengono memorizzate sul server e richiamate tramite un identificatore univoco (hash).

Implementazione con Apollo

  1. Installazione:
npm install apollo-link-persisted-queries
  1. Configurazione delle Persisted Queries:
const { createPersistedQueryLink } = require("apollo-link-persisted-queries");
const { ApolloClient, HttpLink, InMemoryCache } = require("@apollo/client");

const client = new ApolloClient({
  cache: new InMemoryCache(),
  link: createPersistedQueryLink().concat(
    new HttpLink({ uri: "http://localhost:4000/graphql" })
  ),
});

Vantaggi:

  • Sicurezza migliorata: Solo le query pre-approvate possono essere eseguite.
  • Riduzione del payload: I client inviano solo un identificatore della query invece dell’intera query, riducendo la possibilità di attacchi basati su query malevoli.

5. Mascheramento degli Errori

Gli errori GraphQL possono esporre troppe informazioni su come funziona internamente il server, rendendo più facile agli attaccanti identificare punti deboli. Ad esempio, messaggi di errore dettagliati che rivelano struttura del database o stack trace possono essere pericolosi in produzione.

Soluzione: Mascherare i Messaggi di Errore

Personalizza i messaggi di errore restituendo solo informazioni generiche al client, senza esporre dettagli del sistema interno.

Implementazione con Apollo Server

const { ApolloServer, ApolloError } = require("apollo-server");

const formatError = (err) => {
  if (err.message.startsWith("Database Error: ")) {
    return new ApolloError("Errore interno del server");
  }
  return err;
};

const server = new ApolloServer({
  typeDefs,
  resolvers,
  formatError,
});

Vantaggi:

  • Riduzione del rischio: Minimizza l’esposizione di informazioni interne.
  • Messaggi di errore sicuri: Fornisce informazioni utili al client senza rivelare dettagli del backend.

6. Tracciamento e Monitoraggio delle API

Monitorare le API GraphQL in tempo reale ti permette di rilevare attacchi o anomalie di traffico. Strumenti come Apollo Studio, Prometheus e Datadog possono aiutarti a tracciare l’uso delle API e individuare comportamenti sospetti.

Soluzione: Monitoraggio con Apollo Studio

  1. Configurare Apollo Studio:
const { ApolloServerPlugin

UsageReporting } = require('apollo-server-core');

const server = new ApolloServer({
  typeDefs,
  resolvers,
  plugins: [ApolloServerPluginUsageReporting({ apiKey: 'YOUR_API_KEY' })],
});
  1. Monitoraggio delle richieste: Apollo Studio ti consente di tracciare le query eseguite, il tempo di esecuzione e gli errori in tempo reale, dandoti visibilità su potenziali problemi di sicurezza.

Vantaggi:

  • Rilevamento proattivo: Identifica attacchi o traffico anomalo in tempo reale.
  • Miglior controllo: Puoi visualizzare le performance e gli errori per risolvere rapidamente eventuali vulnerabilità.

Conclusione

Le API GraphQL offrono molte potenzialità, ma richiedono anche misure di sicurezza robuste per prevenire attacchi e abusi. Limitare la complessità delle query, implementare rate limiting, usare autenticazione e autorizzazione tramite JWT, e applicare query whitelisting sono tutte tecniche fondamentali per proteggere le API.

Monitorare attivamente l’uso delle API e mascherare i messaggi di errore sono ulteriori passaggi per rafforzare la sicurezza e prevenire la divulgazione di informazioni sensibili. Seguendo queste best practices, puoi proteggere le tue API GraphQL e offrire un servizio sicuro e affidabile ai tuoi utenti.