Cómo crear una API GraphQL segura con Node.js
Lawrence Eagles
29 de marzo de 2022
0 minutos de lecturaGraphQL ofrece seguridad integrada desde el inicio gracias a la validación y la verificación de tipos. Sin embargo, no aborda por completo los problemas de seguridad relacionados con las API. En este artículo, aprenderemos a proteger las API GraphQL creando una aplicación sencilla de Node.js con Fastify y GraphQL.
Según su documentación oficial, GraphQL es un lenguaje de consulta de grafos para API y un entorno de ejecución que permite procesar esas consultas con nuestros datos. GraphQL describe con claridad los datos de nuestra API y nos permite crear API rápidas y flexibles, a la vez que les da a los clientes control total para describir los datos que necesitan. Al igual que REST, GraphQL funciona sobre HTTP, por lo que no depende de una base de datos y es compatible con cualquier lenguaje de backend o cliente.
Fastify es un framework de Node.js basado en complementos, muy eficiente y de alto rendimiento, ideal para crear servidores HTTP rápidos. Inspirado en Hapi y Express, Fastify ofrece una alternativa más amigable para los desarrolladores y con mejor rendimiento, además de una baja sobrecarga.
Fastify es compatible con GraphQL mediante el complemento Mercurius. Mercurius es un adaptador de GraphQL configurable para Fastify; conoceremos más sobre él en las siguientes secciones.
Empecemos con los requisitos previos.
Requisitos previos
Estos son los requisitos previos para este artículo:
Node.js versión 12 o posterior
Conocimientos básicos de JavaScript
Conocimientos básicos de GraphQL
Primeros pasos
Para empezar, debemos crear un servidor básico de Node.js.
Crea un proyecto inicial con una carpeta para el proyecto y, desde esa carpeta, ejecuta el siguiente código en la interfaz de línea de comandos (CLI). Esto iniciará nuestra aplicación e instalará las dependencias necesarias.
Este proyecto usa una versión específica de Mercurius. Para instalarla, ejecuta el siguiente comando:
A continuación, habilitamos los módulos ES6 para usar el sistema estándar de módulos de JavaScript en lugar de commonJS. Para ello, agregamos "type": "module" al archivo package.json.
Luego, actualizamos los scripts de NPM con un comando para iniciar el servidor de Node.js. Para ello, abrimos el archivo package.json y editamos la sección de scripts de la siguiente manera:
Ten en cuenta que el fragmento --es-module-specifier-resolution=node es necesario para habilitar la interoperabilidad entre los módulos ES y los módulos commonJS de Node.
Ahora, crea un directorio src en el directorio raíz. Dentro de src, crea una carpeta graphql que contenga los archivos schema.js y resolvers.js. Agrega el siguiente código al archivo schema.js:
Ahora, agrega el siguiente código al archivo resolvers.js:
Actualizaremos schema.js y agregaremos el archivo resolvers.js en la siguiente sección. Pero debemos crearlos ahora con algo de código básico, ya que son necesarios para que nuestro servidor funcione correctamente.
En el directorio src, crea un archivo index.js con el siguiente código:
El código anterior crea un servidor Fastify principal y registra el complemento Mercurius con estas opciones: schema, resolvers, graphiql y queryDepth.
Ahora podemos iniciar el servidor ejecutando npm run dev. El resultado es el siguiente:
Ahora podemos ver que nuestro servidor funciona. En la siguiente sección, empezaremos a crear nuestras API de blog con GraphQL.
Cómo crear una API de blog segura con Fastify y GraphQL
Hay varias estrategias para proteger una API. Entre ellas se incluyen:
Autenticación y autorización: La autenticación consiste en confirmar que un usuario es quien dice ser, mientras que la autorización se relaciona con los permisos. La autenticación determina si un usuario puede iniciar sesión y, después, lo recuerda. La autorización determina qué permisos se asignan a un usuario identificado y regula si puede realizar operaciones como crear, leer, actualizar o eliminar.
Ocultar errores: Evitar revelar información exacta sobre un error del servidor para no proporcionar al cliente, sin querer, detalles que podrían exponer las vulnerabilidades del servidor.
Límite de profundidad de las consultas: Especificar una profundidad máxima para las consultas de GraphQL. Las consultas profundamente anidadas son peligrosas porque consumen muchos recursos y son costosas de procesar. Como consecuencia, pueden hacer que nuestras API fallen.
Sanitización y validación de entradas: Usa técnicas estándar de seguridad web para evitar que los usuarios envíen datos maliciosos. Aprovecharemos la validación integrada de GraphQL en nuestra aplicación.
En este artículo, crearemos y protegeremos nuestras API con las estrategias anteriores mediante Mercurius y el complemento Mercurius Auth.
Para nuestros fines, el complemento Mercurius Auth tiene dos funciones principales. Primero, nos permite definir directivas de autenticación personalizadas en los campos de nuestro esquema. Las directivas de autenticación son cadenas que se usan como identificadores de los campos protegidos de nuestro esquema.
Además, nos permite aplicar políticas de autenticación personalizadas a estos campos protegidos al realizar una solicitud de GraphQL.
Empezaremos creando datos de prueba. En el directorio src, crea una carpeta data que contenga un archivo index.js con el siguiente código:
A continuación, configuramos nuestro schema reemplazando el código básico del archivo schema.js por el siguiente:
En el código anterior, creamos nuestro esquema de GraphQL y definimos directivas de autenticación para los campos user y users. Enseguida, aplicaremos políticas personalizadas a estos campos protegidos.
Ahora agregamos nuestros resolutores reemplazando el código básico del archivo resolvers.js por el siguiente:
El código anterior contiene resolutores para gestionar las consultas user, users y login.
Por último, debemos agregar una política de autenticación personalizada registrando el complemento Mercurius Auth. Para hacerlo, agregamos el siguiente código debajo del comentario register auth policy en el archivo index.js, dentro del directorio src. El comentario está en la línea 19:
En nuestra política personalizada anterior, el método authContext recupera el token del usuario en los encabezados, mientras que el método applyPolicy contiene las políticas personalizadas de autenticación y autorización.
Además, cuando falla la autenticación o autorización de un usuario, generamos un error con un mensaje genérico, como «Ocurrió un error. ¡Inténtalo de nuevo!». Este mensaje se muestra en lugar de un mensaje de error detallado del servidor, que podría exponer vulnerabilidades existentes.
Con esto, terminamos nuestro trabajo. En la siguiente sección, probaremos nuestras API.
Probar la API
Primero, iniciamos el servidor ejecutando npm run dev desde el directorio raíz. Luego, abrimos el entorno de GraphQL en http://localhost:4500/playground.
Ahora, cuando consultamos una API protegida, como users o user, obtenemos un error como el que se muestra a continuación:

Para que nuestra consulta se ejecute correctamente, debemos autenticarnos. Iniciemos sesión para obtener un token.
Para iniciar sesión, abre una pestaña nueva en el entorno de GraphQL y ejecuta la siguiente consulta:
Si la consulta se ejecuta correctamente, se generará un token y se devolverá como se muestra en la siguiente imagen.

Ten en cuenta que los datos del usuario usados para iniciar sesión ya están en el archivo index.js, dentro del directorio data.
Si intentas iniciar sesión con datos de usuario que no están en ese archivo (como el nombre de usuario «John1Doe»), se producirá un error, como se muestra a continuación:

Ahora, si incluimos nuestro token en los encabezados como x-user, podemos consultar correctamente nuestras API protegidas, como se muestra a continuación. Asegúrate de copiar el token recibido en la consulta de inicio de sesión y usarlo como valor de x-user.
Consulta users
Esta es una consulta users de ejemplo:
Agrega tu token como valor del parámetro del encabezado HTTP x-user, como se muestra a continuación:
El resultado es el siguiente:

Consulta user
Esta es una consulta user de ejemplo:
Agrega tu token como valor del parámetro del encabezado HTTP x-user, como se muestra a continuación:
El resultado es el siguiente:

Conclusión
En este artículo, descubrimos lo fácil que es proteger las API GraphQL con Fastify y GraphQL para crear una aplicación sencilla de Node.js.
Como vimos, GraphQL incluye algunas medidas de seguridad, como la validación y la verificación de tipos. Sin embargo, la flexibilidad y la capacidad que permiten a los usuarios solicitar datos a voluntad hacen que la seguridad siempre deba ser una prioridad.
En este artículo también revisamos algunas estrategias para proteger las API GraphQL: autenticación y autorización, límite de profundidad de las consultas, ocultación de errores y sanitización y validación de entradas.
Estas estrategias de seguridad son muy eficaces, pero podemos agregar más protección con otros métodos, como los tiempos de espera de las consultas y los límites de frecuencia, que especifican cuántas veces puede consultar un cliente una API en cada período.
Empieza con Capture the Flag
Aprende a resolver desafíos de Capture the Flag viendo nuestro taller virtual introductorio a pedido.
Prueba nuestra herramienta gratuita en línea de revisión de código JavaScript para ver cómo Snyk Code analiza tu código en busca de problemas de seguridad y calidad.
