Fix them immediately if you’re using GraphQL Java Kickstart.
GraphQL Java APIs face unique security risks due to query flexibility.
Unlike REST, a single malicious query can expose sensitive data or crash your server.
This guide shows exactly how to fix the 7 most critical vulnerabilities.
🔒 Free: GraphQL Security Configuration Templates
Download ready-to-use Java classes for depth limiting, rate limiting, and authorization (copy-paste ready)
1. Query Depth Attacks
Problem: Deeply nested queries can crash your server.
Fix: Limit query depth to 10 levels.
@Component
public class QueryDepthInstrumentation implements Instrumentation {
private final int maxDepth = 10;
@Override
public InstrumentationContext<ExecutionResult> beginExecution(
InstrumentationExecutionParameters parameters) {
QueryTraverser traverser = QueryTraverser.newQueryTraverser()
.schema(parameters.getSchema())
.document(parameters.getQuery())
.build();
int depth = traverser.reducePreOrder((env, acc) ->
Math.max(acc, env.getParentEnvironment() == null ? 1 : acc + 1), 0);
if (depth > maxDepth) {
throw new ValidationException("Query depth exceeds: " + maxDepth);
}
return super.beginExecution(parameters);
}
}
2. Introspection Abuse
Problem: Attackers can see your entire schema.
Fix: Disable introspection in production.
@Configuration
@Profile("production")
public class ProductionGraphQLConfig {
@Bean
public GraphQLSchema graphQLSchema() {
return GraphQLSchema.newSchema()
.query(queryType)
.mutation(mutationType)
.fieldVisibility(NoIntrospectionGraphqlFieldVisibility.NO_INTROSPECTION_FIELD_VISIBILITY)
.build();
}
}
3. SQL Injection
Problem: User input in queries → database breach.
Fix: Use parameterized queries + validation.
// BAD (vulnerable)
String sql = "SELECT * FROM products WHERE name LIKE '%" + searchTerm + "%'";
// GOOD (safe)
String sql = "SELECT * FROM products WHERE name LIKE ?";
return jdbcTemplate.query(sql, new Object[]{"%" + searchTerm + "%"}, mapper);
4. Field-Level Authorization Bypass
Problem: Users see fields they shouldn’t access.
Fix: Check permissions in every resolver.
@SchemaMapping
public String email(User user, Authentication auth) {
if (!authService.canViewEmail(auth.getName(), user.getId())) {
throw new AccessDeniedException("Insufficient permissions");
}
return user.getEmail();
}
5. Batching Attacks
Problem: 100 queries in one batch → server crash.
Fix: Limit batch size to 10.
@Component
public class BatchLimitingInstrumentation implements Instrumentation {
private final int maxBatchSize = 10;
@Override
public InstrumentationContext<ExecutionResult> beginExecution(...) {
List<ExecutionInput> batch = parameters.getExecutionInput().getBatch();
if (batch != null && batch.size() > maxBatchSize) {
throw new ValidationException("Batch size exceeds: " + maxBatchSize);
}
return super.beginExecution(parameters);
}
}
6. Missing Input Validation
Problem: Malicious arguments → data leaks.
Fix: Validate all inputs with @Valid.
@SchemaMapping
public Product createProduct(@Valid ProductInput input) {
Set<ConstraintViolation<ProductInput>> violations = validator.validate(input);
if (!violations.isEmpty()) {
throw new ValidationException("Validation failed");
}
return productService.createProduct(input);
}
7. Repeated Queries (DoS)
Problem: Same heavy query → server overload.
Fix: Implement rate limiting.
@Component
public class GraphQLRateLimiter {
public boolean tryConsume(String userId, ExecutionInput input) {
int queryCost = calculateQueryCost(input);
return rateLimiter.tryAcquire("rate_limit:" + userId, queryCost);
}
}
Security Checklist
- ☐ Set maximum query depth (recommended: 10)
- ☐ Disable introspection in production
- ☐ Implement field-level authorization
- ☐ Use parameterized queries
- ☐ Validate all input arguments
- ☐ Limit batch size to 10
- ☐ Implement rate limiting
- ☐ Monitor authentication failures
Recommended Tools
Test your API security with these tools:
- Apollo Studio — Schema validation and security scanning
- Snyk — Dependency vulnerability scanning
