Graphql Introspection in Adonisjs with Dynamodb
Graphql Introspection in Adonisjs with Dynamodb — how this specific combination creates or exposes the vulnerability
GraphQL introspection is a feature that allows clients to query the schema for types, queries, and mutations. In AdonisJS, when GraphQL is implemented—commonly via packages such as adonisjs-graphql-client or similar integrations—and backed by DynamoDB as the persistence layer, introspection is often enabled in development or misconfigured in production. This exposes the full schema, including query names, arguments, and response shapes, without authentication.
DynamoDB itself does not enforce schema definitions at the GraphQL layer; the schema is defined in AdonisJS code and passed to the GraphQL server. If introspection is not explicitly disabled, an attacker can send an introspection query (standard GraphQL operation) to the endpoint and retrieve the entire API structure. This reveals sensitive data models, such as which DynamoDB tables are queried and the shape of returned items, including potential PII fields.
When combined, AdonisJS (as the framework exposing the GraphQL endpoint) and DynamoDB (as the data store) create a risk where introspection provides an attacker with knowledge of data access patterns. For example, an introspection query can reveal a user query that fetches items from a DynamoDB table where partition keys align with user identifiers. This supports further attacks like BOLA/IDOR, where guessed identifiers map to DynamoDB items. Introspection does not require authentication in AdonisJS unless explicitly guarded, and DynamoDB’s lack of native schema awareness means the framework must enforce access controls, which introspection bypasses by exposing those controls.
Real-world attack patterns include using introspection output to identify fields that may return API keys or tokens stored as attributes in DynamoDB items, or to discover query names that map to privileged operations. While DynamoDB handles data storage and access permissions, the GraphQL layer in AdonisJS defines what is queryable; introspection exposes that definition, increasing the risk of information leakage even when DynamoDB enforces its own policies.
Dynamodb-Specific Remediation in Adonisjs — concrete code fixes
To mitigate GraphQL introspection risks in AdonisJS when using DynamoDB, disable introspection in production and enforce strict schema and access controls. Below are specific, actionable fixes with working code examples.
1. Disable introspection in production
Configure your GraphQL server to disable introspection when not in development. Using a typical setup (e.g., apollo-server or graphql-http), set introspection to false outside development.
const { ApolloServer } = require('apollo-server-adonis')
const server = new ApolloServer({
typeDefs,
resolvers,
introspection: process.env.NODE_ENV !== 'production',
})
2. Validate and scope DynamoDB queries in resolvers
Ensure resolvers reference only intended DynamoDB operations and enforce ownership at the data layer. Use the AWS SDK for JavaScript to interact with DynamoDB, and apply key condition expressions and filters rigorously.
const { DynamoDBClient, GetItemCommand } = require('@aws-sdk/client-dynamodb')
const { unmarshall } = require('@aws-sdk/util-dynamodb')
class UserResolver {
constructor() {
this.client = new DynamoDBClient({ region: 'us-east-1' })
}
async user(_, { id }, { auth }) {
const userId = auth?.userId
if (!userId || userId !== id) {
throw new Error('Unauthorized')
}
const cmd = new GetItemCommand({
TableName: process.env.USERS_TABLE,
Key: {
userId: { S: id },
},
})
const resp = await this.client.send(cmd)
return resp.Item ? unmarshall(resp.Item) : null
}
}
3. Use environment-based schema composition
Compose your GraphQL schema conditionally to include or exclude types and queries based on environment, reducing exposure in production.
let typeDefs = gql`
type User {
id: ID!
email: String!
}
type Query {
user(id: ID!): User
}
`
if (process.env.NODE_ENV !== 'production') {
typeDefs = gql`
${typeDefs}
type Query {
_schema: String
}
`
}
4. Enforce authorization before DynamoDB access
Implement checks in resolvers to verify that the requesting user has permission to access or modify specific DynamoDB items. Never rely on client-supplied parameters alone for access decisions.
const { DynamoDBClient, QueryCommand } = require('@aws-sdk/client-dynamodb')
const { unmarshall } = require('@aws-sdk/util-dynamodb')
class PostResolver {
constructor() {
this.client = new DynamoDBClient({ region: 'us-east-1' })
}
async posts(_, __, { auth }) {
if (!auth?.userId) {
throw new Error('Unauthorized')
}
const cmd = new QueryCommand({
TableName: process.env.POSTS_TABLE,
KeyConditionExpression: 'userId = :uid',
ExpressionAttributeValues: {
':uid': { S: auth.userId },
},
})
const resp = await this.client.send(cmd)
return resp.Items.map(unmarshall)
}
}
These steps reduce the attack surface by limiting introspection, tightening DynamoDB query scoping, and ensuring authorization checks are performed in AdonisJS resolvers.
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 |