Proteja su API GraphQL de vulnerabilidades

Hola Habr! Le presento la traducción del artículo Protección de su API GraphQL de vulnerabilidades de seguridad .


GraphQL se está convirtiendo rápidamente en la elección de los desarrolladores que necesitan crear una API para su aplicación cliente. Pero, como todas las nuevas tecnologías, GraphQL está sujeto a algunos riesgos de seguridad inherentes. Independientemente de si está creando un proyecto de terceros o una aplicación empresarial a gran escala, debe asegurarse de protegerse de estas amenazas.


imagen


Aunque las amenazas enumeradas en esta publicación son específicas de GraphQL, su implementación introducirá un nuevo conjunto de amenazas que deberán abordarse. También es importante que comprenda las amenazas a las que están expuestas las aplicaciones que se ejecutan en la red.


Amenaza: consultas grandes y profundamente anidadas que son caras de calcular


Solución : limitación de profundidad de anidamiento


El poder proporcionado por GraphQL está asociado con algunas nuevas amenazas de seguridad. Las más comunes son consultas profundamente anidadas, que conducen a cálculos costosos y enormes JSON, que pueden afectar negativamente a la red y su ancho de banda.


La forma correcta de proteger su API de este tipo de ataque es limitar la profundidad de las solicitudes para que las solicitudes profundas maliciosas se bloqueen hasta que se calcule el resultado.


El límite de profundidad de GraphQL proporciona una interfaz simple para limitar la profundidad de la consulta.


import depthLimit from 'graphql-depth-limit' import express from 'express' import graphqlHTTP from 'express-graphql' import schema from './schema' const app = express() app.use('/graphql', graphqlHTTP((req, res) => ({ schema, validationRules: [ depthLimit(10) ] }))) 

Amenaza: solicitudes de mutación vulnerable por fuerza bruta


Solución : limite el número de solicitudes


Buscar inicios de sesión y contraseñas es el truco más antiguo en la historia de la piratería. En la última década, se produjeron tantas filtraciones de datos en Internet que recientemente se encontró una base de datos de 772,904,991 correos electrónicos únicos y 21,222,975 contraseñas únicas . Para verificar si se había filtrado información sobre su correo electrónico y contraseña, Troy Hunt incluso creó un sitio web de Have I was Pwned , para el cual, entre otros, utilizó esta base de datos.


Afortunadamente, tiene una manera fácil de hacer que la búsqueda sea realmente difícil y costosa para los atacantes, lo que lo convertirá en un objetivo menos atractivo para ellos.


El complemento Límite de velocidad de GraphQL le permite especificar restricciones para sus consultas de tres maneras diferentes: usando directivas Graphql-shield personalizadas o usando la función para limitar el número base de consultas.


Este complemento le permitirá establecer una ventana de tiempo y un límite en el número de solicitudes. Establecer una ventana de tiempo grande para solicitudes muy vulnerables, como iniciar sesión, y ventanas de tiempo pequeñas para solicitudes menos vulnerables lo ayudará a mantener una experiencia agradable para los usuarios comunes y se convertirá en una pesadilla para los atacantes.


Cree una directiva para limitar el número de solicitudes:


Aquí necesitará un identificador único para cada solicitud. Puede usar la dirección IP del usuario u otro identificador, que es único para cada usuario y corresponde a cada solicitud.


 const rateLimitDirective = createRateLimitDirective({ identifyContext: (context) => { return context.id }, }) 

Agregue una directiva a su esquema:


 import { createRateLimitDirective } from 'graphql-rate-limit' export const schema = { typeDefs, resolvers, schemaDirectives: { rateLimit: rateLimitDirective, }, } export default schema 

Finalmente, agregue una directiva a su consulta vulnerable:


 #        60  Login(input: LoginInput!): User @rateLimit( window: "60s" max: 10 message: "You are doing that too often. Please wait 60 seconds before trying again." ) 

Amenaza: permitir que el usuario influya en datos específicos del usuario


Solución : tome estos datos de una sesión de usuario cuando sea posible


Es fácil suponer que si desea permitir que el usuario actualice un recurso, entonces vale la pena dejar que el usuario decida qué recurso desea actualizar. Pero, ¿qué sucede si un usuario obtiene un identificador de recursos al que realmente no debería tener acceso?


Supongamos que tenemos una solicitud de mutación updateUser que permite al usuario actualizar su perfil.


 mutation UpdateUser($input: {"id": "test123" , "email": "test@example.com"}) { UpdateUser(input: $input) { id firstName lastName } } 

Si no hay protección en el lado del servidor, el atacante, que tiene una lista de identificadores, podría actualizar la dirección de correo electrónico de cualquier usuario. La solución obvia aquí es agregar una verificación para asegurarse de que la ID del usuario actual coincida con la ID en los campos de entrada.


No hagas esto:


 function updateUser({ id, email }) { return User.findOneAndUpdate({ _id: id }, { email }) .catch(error => { throw error; }); } 

Una forma menos obvia pero correcta de resolver este problema es evitar que el identificador se use como entrada y usar el identificador de usuario del objeto de contexto.


Haz esto:


 function updateUser({ email }, context) { return User.findOneAndUpdate({ _id: context.user._id }, { email }) .catch(error => { throw error; }); } 

Quizás este sea un ejemplo bastante trivial, pero realizar tales acciones para cada uno de los objetos con los que el usuario interactúa directamente puede protegerlo de muchos errores riesgosos.


Cumplimiento de varias consultas costosas simultáneamente


Solución : límite de costo de consulta


Al asignar un precio a cada solicitud e indicar el precio máximo por solicitud, podemos protegernos de los intrusos que pueden tratar de cumplir demasiadas solicitudes costosas al mismo tiempo.


El complemento GraphQL Cost Analysis es una manera fácil de especificar el costo de las consultas y el límite de costo máximo.


Determine el costo máximo:


 app.use( '/graphql', graphqlExpress(req => { return { schema, rootValue: null, validationRules: [ costAnalysis({ variables: req.body.variables, maximumCost: 1000, }), ], } }) ) 

Determine el costo de cada solicitud:


 Query: { Article: { multipliers: ['limit'], useMultipliers: true, complexity: 3, }, } 

Amenaza: revelar detalles de implementación de GraphQL


Solución : deshabilite la introspección en el código de "combate"


GraphQL es una herramienta de desarrollo extremadamente útil. Es tan poderoso que incluso documenta sus esquemas, solicitudes y suscripciones por usted. Esta información puede ser una mina de oro para los atacantes que desean encontrar vulnerabilidades en su aplicación.


El complemento Graphtros Display Introspection evitará que su esquema se filtre al público. Simplemente importe el complemento y aplíquelo a sus reglas de validación.


 import express from 'express'; import bodyParser from 'body-parser'; import { graphqlExpress } from 'graphql-server-express'; + import NoIntrospection from 'graphql-disable-introspection'; const myGraphQLSchema = // ...    ! const PORT = 3000; var app = express(); app.use('/graphql', bodyParser.json(), graphqlExpress({ schema: myGraphQLSchema, validationRules: [NoIntrospection] })); app.listen(PORT); 

Source: https://habr.com/ru/post/481840/


All Articles