Graphql Introspection in Flask
How Graphql Introspection Manifests in Flask
Graphql introspection is a powerful feature that allows clients to query the schema of a GraphQL API to discover available types, queries, mutations, and fields. While invaluable for development and debugging, exposed introspection endpoints in production Flask applications create significant attack surface. Attackers can leverage introspection to map out your entire API structure, identify vulnerable fields, and craft targeted attacks.
In Flask, GraphQL introspection typically manifests through the /graphql or /api endpoints that handle GraphQL requests. When the introspection query is sent without proper restrictions, the server returns the complete schema definition. This includes all object types, their fields, arguments, and even internal implementation details that should remain hidden from external clients.
A common Flask implementation using the flask-graphql library might look like this:
from flask import Flask, request, jsonify
from flask_graphql import GraphQLView
from graphene import ObjectType, String, Schema
class Query(ObjectType):
hello = String(name=String(default_value="stranger"))
def resolve_hello(root, info, name):
return f'Hello {name}'
schema = Schema(query=Query)
app = Flask(__name__)
app.add_url_rule('/graphql', view_func=GraphQLView.as_view('graphql', schema=schema, graphiql=True))
if __name__ == '__main__':
app.run()This basic setup exposes introspection by default. An attacker can send a POST request with the introspection query:
{
"query": """{
__schema {
types {
name
fields {
name
args {
name
description
}
}
}
}
}"""
}The response reveals every type, field, and argument in your schema. For Flask applications handling sensitive operations like user management or financial transactions, this information becomes ammunition for targeted attacks. An attacker might discover admin-only mutations, identify fields that accept dangerous inputs, or find relationships between objects that enable privilege escalation.
Flask-specific introspection vulnerabilities often appear in applications using multiple GraphQL libraries or custom implementations. Developers might enable GraphiQL for debugging, forget to disable it in production, or implement custom introspection endpoints that bypass standard protections. The combination of Flask's flexibility and GraphQL's introspection capabilities creates scenarios where developers accidentally expose more than intended.
Flask-Specific Detection
Detecting GraphQL introspection vulnerabilities in Flask requires both manual testing and automated scanning. The most straightforward approach is to send an introspection query to your GraphQL endpoint and verify the response. Using curl or Postman, you can test:
curl -X POST http://localhost:5000/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{__schema {types {name}}}'If the response contains type information, introspection is enabled. For Flask applications using flask-graphql, you can check if GraphiQL is enabled in your view configuration:
app.add_url_rule('/graphql', view_func=GraphQLView.as_view('graphql', schema=schema, graphiql=True)) # DANGEROUS in productionmiddleBrick provides automated detection for GraphQL introspection vulnerabilities in Flask applications. The scanner sends standard introspection queries to GraphQL endpoints and analyzes the responses for schema information disclosure. It identifies Flask-specific patterns like GraphiQL endpoints, custom introspection implementations, and schema exposure through error messages.
middleBrick's GraphQL security checks include:
- Introspection query detection and response analysis
- GraphiQL endpoint discovery
- Schema information disclosure assessment
- Field-level access control verification
- Rate limiting bypass attempts
The scanner tests multiple introspection query variations to ensure comprehensive coverage. It also checks for indirect introspection exposure through GraphQL error messages that might reveal schema details when queries fail. For Flask applications, middleBrick specifically looks for common patterns like graphiql=True configurations and custom GraphQL view implementations that might have overlooked introspection protections.
Beyond automated scanning, Flask developers should implement logging and monitoring for GraphQL endpoint access. Unusual query patterns, repeated introspection attempts, or access from unexpected IP addresses might indicate reconnaissance activities. Flask's built-in logging can capture GraphQL requests, and middleware can track query complexity and frequency.
Flask-Specific Remediation
Remediating GraphQL introspection vulnerabilities in Flask requires a layered approach. The most direct solution is disabling introspection in production environments. With flask-graphql, you can control introspection behavior:
from flask import Flask
from flask_graphql import GraphQLView
from graphene import ObjectType, String, Schema
class Query(ObjectType):
hello = String(name=String(default_value="stranger"))
def resolve_hello(root, info, name):
return f'Hello {name}'
schema = Schema(query=Query)
app = Flask(__name__)
# Disable introspection in production
app.add_url_rule('/graphql', view_func=GraphQLView.as_view('graphql', schema=schema, graphiql=False, allow_introspection=False))
if __name__ == '__main__':
app.run()This approach completely blocks introspection queries, but it also prevents legitimate development tools from working. A more nuanced solution involves environment-based introspection control:
import os
from flask import Flask
from flask_graphql import GraphQLView
from graphene import ObjectType, String, Schema
class Query(ObjectType):
hello = String(name=String(default_value="stranger"))
def resolve_hello(root, info, name):
return f'Hello {name}'
schema = Schema(query=Query)
app = Flask(__name__)
# Enable introspection only in development
allow_introspection = os.getenv('FLASK_ENV') == 'development'
graphiql_enabled = allow_introspection
app.add_url_rule('/graphql', view_func=GraphQLView.as_view('graphql', schema=schema, graphiql=graphiql_enabled, allow_introspection=allow_introspection))
if __name__ == '__main__':
app.run()For applications requiring introspection in production (such as those with internal development tools), implement authentication and authorization controls:
from functools import wraps
from flask import request, abort
def authenticated_introspection(f):
@wraps(f)
def decorated_function(*args, **kwargs):
# Only allow introspection for authenticated users with specific roles
auth_header = request.headers.get('Authorization')
if not auth_header or not is_authorized_user(auth_header):
# Block introspection queries but allow other operations
if is_introspection_query(request.get_json()):
abort(403)
return f(*args, **kwargs)
return decorated_function
def is_introspection_query(payload):
query = payload.get('query', '')
introspection_patterns = ['__schema', '__type', '__typename']
return any(pattern in query for pattern in introspection_patterns)
app.add_url_rule('/graphql', view_func=authenticated_introspection(GraphQLView.as_view('graphql', schema=schema, graphiql=False)))Additional Flask-specific protections include implementing query complexity analysis to prevent expensive introspection queries, rate limiting GraphQL endpoints to reduce reconnaissance effectiveness, and using middleware to sanitize error messages that might leak schema information. The graphql-ws library provides built-in protections against introspection abuse, and custom schema directives can enforce field-level access controls based on user roles.
Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |
Frequently Asked Questions
How can I test if my Flask GraphQL endpoint has introspection enabled?
curl -X POST http://your-flask-app/graphql -H "Content-Type: application/json" -d '{"query": "__schema {types {name}}"}'. Alternatively, use middleBrick's automated scanning to detect introspection vulnerabilities without manual testing.