Graphql Introspection in Adonisjs with Firestore
Graphql Introspection in Adonisjs with Firestore — how this specific combination creates or exposes the vulnerability
GraphQL introspection allows clients to query the schema for types, queries, and mutations. In an AdonisJS application that uses Firestore as the backend, enabling introspection in production exposes structural details about your GraphQL API and the underlying data model. This can reveal field names, relationships, and query patterns that assist an attacker in crafting targeted operations such as excessive nested queries or IDOR probes.
When GraphQL introspection is active and the server also exposes an unauthenticated or weakly authenticated endpoint, the combination increases the attack surface. AdonisJS does not enable introspection by default in production, but if a developer accidentally leaves it enabled or configures the GraphQL server to serve introspection queries without authentication, an attacker can use standard GraphQL introspection queries to map the API surface. This mapping can be combined with Firestore-specific behaviors—such as predictable document IDs or loose security rules—to probe for BOLA/IDOR and data exposure issues.
For example, an attacker can run an introspection query to list all types and then attempt operations that reference Firestore document paths. If field-level authorization is not enforced consistently, introspection can highlight which queries return sensitive data without proper checks. Because Firestore rules are evaluated at query time, an attacker may try to infer rule logic by observing which queries succeed or fail, especially when testing unauthenticated endpoints.
In a black-box scan, middleBrick runs GraphQL introspection probes as part of its 12 security checks. If introspection is discoverable, the scan will flag it and highlight related risks such as excessive agency or data exposure. This helps you identify whether your AdonisJS + Firestore GraphQL endpoint reveals more than intended and provides remediation guidance to reduce exposure.
Firestore-Specific Remediation in Adonisjs — concrete code fixes
To secure GraphQL introspection when using Firestore in AdonisJS, disable introspection in production and enforce strict authorization checks. Below are concrete steps and code examples.
1. Disable introspection in production
Ensure your GraphQL server is configured to reject introspection queries in production. In your AdonisJS provider or server configuration, set introspection to false.
// start/graphql.ts
import { HttpContext } from '@ioc:Adonis/Core/HttpContext'
import { schema } from '@ioc:Adonis/Addons/Gql')
const gql = schema({
typeDefs: /* GraphQL */ `
type Query {
publicPost(id: ID!): Post
}
type Post {
id: ID!
title: String!
author: String!
}
`,
resolvers: {
Query: {
publicPost: async (_, { id }, { auth }) => {
// Authorization check required
const user = auth.getUser()
if (!user) {
throw new Error('Unauthorized')
}
// Fetch from Firestore with user-specific constraints
return fetchPostFromFirestore(id, user.id)
},
},
},
})
export const httpContext = gql.createContext(({ request, auth }: HttpContext) => ({
request,
auth,
introspection: false, // disable introspection in production
}))
2. Enforce Firestore security rules and query constraints
Firestore security rules must validate access at the document level. Combine rules with query constraints in AdonisJS to avoid leaking data.
// firestore.rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /posts/{postId} {
allow read: if request.auth != null
&& request.auth.uid == request.resource.data.authorId;
allow write: if request.auth != null
&& request.auth.uid == request.resource.data.authorId;
}
}
}
In your AdonisJS resolver, ensure you pass the user ID to Firestore queries and avoid fetching all documents:
// app/Helpers/fetchPostFromFirestore.ts
import { Firestore } from '@google-cloud/firestore'
const firestore = new Firestore()
export async function fetchPostFromFirestore(postId: string, userId: string) {
const doc = await firestore
.collection('posts')
.doc(postId)
.get()
if (!doc.exists) {
throw new Error('Post not found')
}
const data = doc.data()
// Enforce ownership at runtime as an additional guard
if (data.authorId !== userId) {
throw new Error('Forbidden: ownership mismatch')
}
return {
id: doc.id,
title: data.title,
author: data.author,
}
}
3. Add rate limiting and input validation
Protect against excessive introspection attempts and malformed queries by adding rate limiting and input validation in your AdonisJS route or controller.
// routes.ts
import Route from '@ioc:Adonis/Core/Route'
import { schema, rules } from '@ioc:Adonis/Core/Validator'
Route.post(
'/graphql',
async ({ request, response, auth }) => {
const bodySchema = schema({
query: schema.string({}, [rules.maxLength(2000)]),
variables: schema.object.optional(),
})
const validated = await request.validate({ schema: bodySchema })
// Proceed with authenticated and authorized execution
const user = auth.getUserOrFail()
// Execute query with Firestore using user context
},
['validate']
)
By disabling introspection, enforcing Firestore document-level rules, and validating inputs, you reduce the risk of exposing data models and prevent unauthorized data probing through GraphQL queries.
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 |