Graphql Introspection in Fastapi with Basic Auth
Graphql Introspection in Fastapi with Basic Auth — how this specific combination creates or exposes the vulnerability
GraphQL introspection is a feature that allows clients to query the schema structure, types, and operations of a GraphQL API. In a FastAPI application, introspection is often left enabled during development and, if unintentionally exposed in production, becomes a valuable source of information for an attacker. When combined with Basic Authentication that is improperly enforced or limited to certain routes, introspection can contribute to an information disclosure path that supports further attacks such as BOLA/IDOR or over-privileged access.
Consider a FastAPI service that exposes a GraphQL endpoint at /graphql and uses HTTP Basic Auth via a dependency that checks a hardcoded user/pass pair. If the introspection query is allowed for unauthenticated requests or for requests that only partially satisfy the auth dependency, an attacker can retrieve the full schema without valid credentials. A typical vulnerable dependency might look like this:
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
import uvicorn
app = FastAPI()
security = HTTPBasic()
# Weak check: only protects specific routes, not the GraphQL endpoint
async def get_current_user(credentials: HTTPBasicCredentials = Depends(security)):
if credentials.username != "admin" or credentials.password != "secret":
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid credentials",
headers={"WWW-Authenticate": "Basic"},
)
return credentials.username
@app.get("/health")
async def health(user: str = Depends(get_current_user)):
return {"status": "ok"}
# Introspection allowed without proper auth enforcement
@app.post("/graphql")
async def graphql(query: str):
# naive handling; introspection query executed regardless of auth
return {"data": "..."}
In this setup, the /graphql route does not consistently apply get_current_user. Even if it does, a client that sends an introspection query such as __schema { queryType { name } } can learn the type names, field definitions, and operation names. This schema knowledge enables crafted POST requests aimed at endpoints that do require auth, aiding in BOLA/IDOR probing or privilege escalation attempts. The vulnerability is not that introspection exists, but that it is reachable in a way that bypasses the intended access controls of the Basic Auth layer.
An attacker can couple this with the BOLA/IDOR check to test known or guessed identifiers (e.g., /users/1, /users/2) using the information from the schema to understand which fields or relations are available. Because Basic Auth transmits credentials in base64 (easily decoded), the transport risk compounds the information exposure when introspection is open.
Basic Auth-Specific Remediation in Fastapi — concrete code fixes
To secure the combination of GraphQL introspection and Basic Auth in FastAPI, enforce authentication uniformly and avoid leaking schema details to unauthenticated contexts. The following example demonstrates a robust approach where the GraphQL endpoint is protected by the same auth dependency used elsewhere, and introspection is either disabled or gated behind authentication.
First, ensure that the auth dependency is applied consistently across all routes that need protection, including the GraphQL route. You can conditionally allow introspection only for authenticated users or disable it entirely in production.
from fastapi import Depends, FastAPI, HTTPException, status, Request
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from starlette.responses import JSONResponse
import uvicorn
app = FastAPI()
security = HTTPBasic()
VALID_USER = {"username": "admin", "password": "secret"}
def get_current_user(credentials: HTTPBasicCredentials = Depends(security)):
if credentials.username != VALID_USER["username"] or credentials.password != VALID_USER["password"]:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid credentials",
headers={"WWW-Authenticate": "Basic"},
)
return credentials.username
@app.get("/health")
async def health(user: str = Depends(get_current_user)):
return {"status": "ok"}
@app.post("/graphql")
async def graphql(
request: Request,
query: str,
user: str = Depends(get_current_user)
):
# Example: reject introspection queries for unauthenticated paths; here auth is required by Depends
if "__schema" in query or "__type" in query:
# Optionally return a sanitized response or a clear error
return JSONResponse({"error": "introspection disabled"}, status_code=403)
# Proceed with normal GraphQL resolution
return {"data": "resolved"}
In this remediation, the get_current_user dependency is mandatory for /graphql. If you choose to allow introspection at all, ensure it is only available to authenticated clients. Alternatively, disable introspection in production by using a schema that does not expose __schema and __type root fields, or by wrapping the GraphQL library configuration to reject introspection queries. Coupling this with transport security (HTTPS) prevents credentials from being trivially decoded and reduces the attack surface exposed by introspection.
For deployments, consider using environment-based configuration so that introspection is disabled when not in development. This reduces the risk of schema leakage while preserving developer workflows. Combining these practices aligns with checks such as Authentication and Property Authorization in scans run by tools like the middleBrick dashboard, which can highlight exposed introspection endpoints and weak auth coverage.
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 |