Graphql Introspection in Fastapi with Dynamodb
Graphql Introspection in Fastapi with Dynamodb — how this specific combination creates or exposes the vulnerability
GraphQL introspection allows clients to query the schema for types, queries, and mutations. In a FastAPI application that uses GraphQL (for example via strawberry or graphene) and routes queries to Amazon DynamoDB, enabling introspection in production exposes the full shape of your data model and query capabilities without authentication. middleBrick detects this as a BOLA/IDOR and Property Authorization finding because introspection can reveal attribute names, key schemas, and resolver logic that would otherwise be internal.
When DynamoDB is used as the backend store, the resolver often constructs query parameters such as KeyConditionExpression or scans based on the resolved fields. If introspection is accessible, an attacker can map which fields are indexed, which are part of the primary key, and which resolvers perform scan operations. This knowledge facilitates targeted BOLA attacks where the attacker iterates over user identifiers and observes differences in response or timing. Even if the GraphQL layer enforces some ownership checks, a misconfigured resolver might inadvertently rely on attributes that are not validated, leading to IDOR via DynamoDB partition/key lookups.
Additionally, because DynamoDB schema definitions are not enforced at the database level, the GraphQL type system becomes the primary guard. Introspection leaking this schema can highlight mismatches between claimed ownership (e.g., a user_id claim in a JWT) and the actual filter logic in resolvers. middleBrick flags this as a finding and maps it to OWASP API Top 10:2023 A1 (Broken Object Level Authorization) and A7 (Identification and Authentication Failures).
Dynamodb-Specific Remediation in Fastapi — concrete code fixes
To reduce risk, disable introspection in production and enforce strict field-level authorization in resolvers. Below are concrete FastAPI examples using the strawberry library with DynamoDB data access layer.
1. Disable introspection in production
Configure the GraphQL endpoint to reject introspection queries when a feature flag or environment variable indicates production. This prevents unauthenticated schema discovery while preserving developer experience locally.
import os
from strawberry.fastapi import GraphQLRouter
import strawberry
from typing import Optional
@strawberry.type
class Query:
@strawberry.field
def get_item(self, user_id: str, item_id: str) -> Optional[str]:
# Resolver with explicit user_id validation
return f"user-{user_id}-item-{item_id}"
schema = strawberry.Schema(query=Query)
# In your FastAPI app, wrap the router with a check
graphql_app = GraphQLRouter(schema)
from fastapi import FastAPI, Request, HTTPException
app = FastAPI()
@app.middleware("http")
async def block_introspection_middleware(request: Request, call_next):
if request.url.path == "/graphql":
body = await request.body()
# Basic introspection query detection
if b'__schema' in body or b'__type' in body:
raise HTTPException(status_code=403, detail="Introspection disabled")
response = await call_next(request)
return response
app.add_route("/graphql", graphql_app)
app.add_websocket_route("/graphql", graphql_app)
2. DynamoDB resolver with strict key validation
Ensure the resolver uses the authenticated user context to form the DynamoDB key, avoiding reliance on client-supplied identifiers for ownership.
import boto3
from strawberry.types import Info
import os
dynamodb = boto3.resource("dynamodb", region_name=os.getenv("AWS_REGION", "us-east-1"))
table_name = os.getenv("DYNAMODB_TABLE", "Items")
table = dynamodb.Table(table_name)
@strawberry.type
class Query:
@strawberry.field
def get_item(self, info: Info, item_id: str) -> Optional[dict]:
user_id = info.context.state.user_id # injected by auth middleware
if not user_id:
raise ValueError("Unauthenticated")
# Key must include both partition key (PK) and sort key (SK) or GSI
response = table.get_item(
Key={
"PK": f"USER#{user_id}",
"SK": f"ITEM#{item_id}",
}
)
return response.get("Item")
3. Avoid Scan and prefer Query with indexed GSI
Resolvers should avoid Scan operations. Define a Global Secondary Index (GSI) for access patterns and reference it in the resolver. This limits the data surface and aligns with DynamoDB best practices.
@strawberry.type
class Query:
@strawberry.field
def list_user_items(self, info: Info) -> list[dict]:
user_id = info.context.state.user_id
if not user_id:
raise ValueError("Unauthenticated")
gsi_name = os.getenv("GSI_NAME", "UserIdIndex")
response = table.query(
IndexName=gsi_name,
KeyConditionExpression=boto3.dynamodb.conditions.Key("user_id").eq(f"USER#{user_id}")
)
return response.get("Items", [])
4. Apply field-level authorization in resolvers
Even with correct keys, enforce per-field authorization within resolvers to prevent over-fetching and ensure users only access their own data patterns that introspection might expose.
@strawberry.type
class ItemType:
id: strawberry.ID
name: str
# Sensitive fields gated behind authorization
metadata: Optional[dict] = None
@strawberry.type
class Query:
@strawberry.field
def item(self, info: Info, item_id: str) -> Optional[ItemType]:
user_id = info.context.state.user_id
item = fetch_item_from_dynamodb(user_id, item_id)
if not item:
return None
# Only include metadata if the user has permission
if user_can_view_metadata(user_id, item_id):
return ItemType(id=item_id, name=item["name"], metadata=item.get("metadata"))
return ItemType(id=item_id, name=item["name"])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 |