Saltar a contenido

GraphQL Circular References

Referencias circulares de GraphQL

Descripción

GraphQL permite a los clientes solicitar datos específicos, y su flexibilidad puede explotarse para crear consultas complejas o recursivas. Las referencias circulares ocurren cuando un tipo de objeto se refiere a sí mismo directa o indirectamente a través de otros tipos.

Por ejemplo:

 query CircularReferences  {
     user {
       friends {
         user {
           friends {
             user {
               __typename
             }
           }
         }
       }
     }
   }

Impacto de seguridad de las referencias circulares en GraphQL:

  • Denegación de servicio: al enviar una consulta grande con demasiadas referencias anidadas, un atacante puede abrumar al servidor, lo que hace que se ralentice o se bloquee.
  • Agotamiento de recursos: el servidor puede quedarse sin recursos de memoria o CPU al procesar la consulta, lo que provoca una degradación del rendimiento o la falta de disponibilidad del servicio.

Recomendación

Para mitigar el riesgo de referencias circulares en GraphQL, puede seguir estas recomendaciones: 1. Limitación de profundidad: Implemente un middleware para verificar la profundidad de la consulta y generar un error si excede el límite. Ejemplo:

class DepthAnalysisMiddleware:
    def resolve(self, next, root, info, **args):
        if info.operation.selection_set:
            depth = 0
            for field in info.operation.selection_set.selections:
                depth = max(depth, self._get_depth(field))
            if depth > 3:
                raise Exception('Query depth is too high')
        return next(root, info, **args)

    def _get_depth(self, field):
        if field.selection_set:
            return 1 + max(self._get_depth(f) for f in field.selection_set.selections)
        return 1
  1. Detección de referencia circular: Rediseñe el esquema para evitar referencias circulares. Ejemplo de referencia circular:
class User(graphene.ObjectType):
    id = graphene.ID()
    name = graphene.String()
    friends = graphene.List(lambda: User)

    def resolve_friends(self, info):
        return [User(id=1, name='Alice'), User(id=2, name='Bob')]

Ejemplo de esquema rediseñado:

  class FriendProfile(graphene.ObjectType):
      id = graphene.ID()
      name = graphene.String()

  class User(graphene.ObjectType):
      id = graphene.ID()
      name = graphene.String()
      friends = graphene.List(FriendProfile)

      def resolve_friends(self, info):
          return [FriendProfile(id=1, name='Alice'), FriendProfile(id=2, name='Bob')]
      const FriendProfile = new GraphQLObjectType({
          name: 'FriendProfile',
          fields: {
              id: { type: GraphQLID },
              name: { type: GraphQLString }
          }
      });

      const User = new GraphQLObjectType({
          name: 'User',
          fields: {
              id: { type: GraphQLID },
              name: { type: GraphQLString },
              friends: { type: new GraphQLList(FriendProfile) }
          }
      });  

Enlaces

Estándares

  • CWE_TOP_25:
    • CWE_400
  • PCI_STANDARDS:
    • REQ_6_2
    • REQ_6_4
    • REQ_11_3
  • OWASP_MASVS_L2:
    • MSTG_PLATFORM_2
  • OWASP_ASVS_L3:
    • V13_4_1
  • SOC2_CONTROLS:
    • CC_2_1
    • CC_4_1
    • CC_7_1
    • CC_7_2
    • CC_7_4
    • CC_7_5
    • CC_9_1
  • HIPAA_CONTROLS:
    • SECURITY212
    • SECURITY213