Graphql Introspection in Flask with Basic Auth
Graphql Introspection in Flask with Basic Auth — how this specific combination creates or exposes the vulnerability
GraphQL introspection in a Flask application exposes the full schema, including types, queries, and mutations. When combined with HTTP Basic Auth, introspection remains available without additional access controls, because the GraphQL endpoint typically responds to introspection queries regardless of authentication state. This creates a risk where an unauthenticated or low-privilege attacker can map the API surface and discover sensitive fields, relationships, and operations that can be leveraged in further attacks.
Basic Auth transmits credentials in an encoded, not encrypted, form unless protected by TLS. If TLS is terminated correctly but access control is not enforced at the GraphQL resolver or schema level, an attacker can pair a captured Authorization header with introspection queries to enumerate the API. Common misconfigurations include allowing introspection in production, failing to disable it conditionally, or not coupling introspection permissions with the authentication layer. For example, a Flask route that passes all requests to a GraphQL view without validating scopes or roles may expose user data structures, mutation inputs, and internal object identifiers through introspection.
In a black-box scan, middleBrick tests GraphQL introspection by attempting to query the schema and cross-referencing findings with Basic Auth–protected endpoints. If introspection returns data alongside a 401 for unauthorized requests, the scan flags Authentication issues and highlights BOLA/IDOR risks when object-level authorization is missing. This combination increases the severity of findings because attackers can enumerate identifiers and then test direct object references. The scanner also checks whether input validation and rate limiting apply to introspection queries, as unrestricted introspection can amplify enumeration attacks.
Real-world attack patterns include using introspection to locate ID fields in queries, then crafting Insecure Direct Object Reference (IDOR) or BOLA attempts with captured credentials. For instance, if introspection reveals a user query like user(id: ID!): User, an attacker can iterate numeric IDs when weak authorization is present. OWASP API Top 10 A1 (Broken Object Level Authorization) and A7 (Rate Limiting Misconfiguration) map to these scenarios, and findings often align with PCI-DSS and SOC2 controls around access to sensitive data.
middleBrick detects these issues by running parallel checks against the unauthenticated surface and, when credentials are provided, testing authenticated paths. Reports include per-category breakdowns, prioritized findings with severity, and remediation guidance. In the Pro plan, continuous monitoring can rescan GraphQL endpoints on a schedule and alert on schema changes that reintroduce exposure.
Basic Auth-Specific Remediation in Flask — concrete code fixes
To secure GraphQL introspection in Flask with Basic Auth, enforce authentication before allowing introspection and ensure credentials are handled safely. Use HTTPS to protect the encoded credentials in transit, and apply strict access controls at the resolver or middleware layer.
Example: Conditional Introspection with Basic Auth
Disable introspection by default and enable it only for authenticated admin users. The following snippet shows a Flask app with Basic Auth and guarded introspection:
from flask import Flask, request, jsonify
import base64
app = Flask(__name__)
VALID_USERS = {
"admin": "strongpassword123",
"analyst": "readonlypass"
}
def check_auth(header_value):
try:
auth_type, encoded = header_value.split(" ", 1)
if auth_type.lower() != "basic":
return None
decoded = base64.b64decode(encoded).decode("utf-8")
username, password = decoded.split(":", 1)
if username in VALID_USERS and VALID_USERS[username] == password:
return username
except Exception:
return None
return None
@app.before_request
def require_auth_for_graphql():
if request.path == "/graphql":
auth_header = request.headers.get("Authorization")
user = check_auth(auth_header) if auth_header else None
if user is None:
return jsonify({"error": "Unauthorized"}), 401
# Optionally attach user to request for resolvers
request.user = user
@app.route("/graphql", methods=["POST"])
def graphql():
from graphql import graphql_sync
from my_schema import schema # your constructed GraphQL schema
data = request.get_json(force=True)
query = data.get("query")
# Example: block introspection for non-admin users
if "__schema" in query and request.user != "admin":
return jsonify({"error": "Introspection not allowed for your role"}), 403
result = graphql_sync(schema, query)
return jsonify(result.to_dict())
if __name__ == "__main__":
app.run(ssl_context="adhoc") # use proper TLS in production
In this pattern, check_auth validates Basic Auth credentials and returns the username or None. The before-request guard rejects unauthenticated requests to the GraphQL endpoint. Within the resolver, introspection queries are blocked for non-admin users, reducing exposure. For production, replace ssl_context="adhoc" with a proper certificate and store credentials securely, for example using environment variables or a secrets manager.
Alternative: Disabling Introspection Entirely
If introspection is not needed in production, disable it at the schema level. With graphql-core, set introspection=False when creating the GraphQL view:
from graphql import GraphQLSchema, build_schema
from graphql.server import GraphQLServer
schema_str = """
type Query {
hello: String
}
"""
schema = build_schema(schema_str)
# In your Flask view, disable introspection
server = GraphQLServer(schema, introspection=False)
Disabling introspection prevents both authenticated and unauthenticated enumeration, which is the strongest mitigation. Combine this with rate limiting and monitoring to detect scanning attempts. The Pro plan’s continuous monitoring can verify that introspection remains disabled across deployments.
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 |