Hoe deze controle werkt
Scanaris stuurt een POST-verzoek met de kleinst mogelijke introspectiequery, {__schema{queryType{name}}}, naar drie paden waar een GraphQL-endpoint meestal leeft: /graphql, /api/graphql en /v1/graphql. Antwoordt een ervan 200 en bevat de body __schema of queryType, dan staat introspectie aan en opent hij een bevinding met gemiddelde ernst en het pad. Is er geen GraphQL op je site, dan antwoordt geen enkel pad zo en is er geen bevinding. De query is alleen-lezen, raakt geen gegevens en valt onder dezelfde tijd- en groottelimieten als de rest van de scan.
Waarom dit belangrijk is
Introspectie is de GraphQL-functie waarmee je de API naar haar eigen schema kunt vragen: elk type, elk veld van elk type, elke beschikbare query en mutatie, met hun argumenten. In ontwikkeling is het onmisbaar: de playground, de autoaanvulling in de editor en codegeneratoren steunen erop. In productie, open voor iedereen, is het een complete kaart van je backend. Een aanvaller ziet dat er een mutatie deleteUser(id) of een veld internalNotes bestaat zonder te hoeven raden, en gaat meteen testen of de autorisatie op elk daarvan goed is gedaan. Daarom gemiddelde ernst: het stelt zelf geen gegevens bloot, maar maakt elke andere rechtenfout triviaal te vinden.
Hoe je het in productie uitschakelt
- Apollo Server:
introspection: "production" !== 'production'bij het aanmaken van de server. Sinds Apollo 4 staat het al uit wanneerNODE_ENVopproductionstaat; zie je het toch aan, controleer dan of die variabele je deployment echt bereikt. Schakel in productie ook de landingspagina of playground uit. - GraphQL Yoga en Envelop: de plugin
useDisableIntrospection()uit@envelop/disable-introspection, afhankelijk van de omgeving. - Hasura (het pad /v1/graphql): de beveiligingsinstellingen van het project bevatten een introspectiecontrole per rol (Cloud en Enterprise). Zorg er ook voor dat de openbare rol minimale rechten heeft en dat het admin-secret nooit naar de browser gaat.
- graphql-ruby:
disable_introspection_entry_pointsin de schemaklasse. - Python (Graphene, Strawberry, Ariadne): voeg in productie de regel
NoSchemaIntrospectionCustomRuleuitgraphql-coretoe aan de validatieregels. - .NET (Hot Chocolate):
.AllowIntrospection(false), of de optie om introspectie per verzoek te sturen op basis van een header of een rol.
Introspectie uitschakelen vervangt geen autorisatie: elke query en mutatie moet controleren wie er vraagt. Combineer het met limieten op querydiepte en -complexiteit (die voorkomen geneste query's die de server platleggen) en schakel, als je server het aanbiedt, ook veldsuggesties uit ("Did you mean…"), die namen lekken zelfs met gesloten introspectie.
Controleer of het opgelost is
curl -s -X POST https://jouwdomein.nl/graphql -H "Content-Type: application/json" -d '{"query":"{__schema{queryType{name}}}"}'
Herhaal met /api/graphql en /v1/graphql. Verwacht: een fout als "introspection is not allowed", een 400 of 403, of een 404 als daar geen GraphQL zit. Zie je "queryType":{"name":"Query"}, dan staat het nog open.
Veelgestelde vragen
Mijn API is openbaar en gedocumenteerd. Geldt dit voor mij?
Als je het schema bewust publiceert, onthult open introspectie niets nieuws en kun je de bevinding als geaccepteerd markeren. De meeste applicatie-API's zitten niet in die situatie: ze hebben interne mutaties die niemand hoort te kennen.
Mijn endpoint zit op een ander pad. Wordt dat niet gecontroleerd?
Alleen de drie meest voorkomende paden worden getest. Zit de jouwe ergens anders, voer dan de test hierboven uit met je echte pad. Andere panelen en ontwikkelendpoints die in productie niet horen te antwoorden, worden gezocht door blootgestelde endpoints.
En als het endpoint authenticatie vereist?
Dan antwoordt introspectie 401 en wordt het niet gemeld. Dat is de juiste situatie: een aanvaller zonder sessie krijgt geen schema. Controleer dat de CORS-header van de API niet open staat voor elke origin; daar kijkt permissieve CORS naar.