# GraphQL Security Detection Module

> GraphQL API Security: Unauthorized field access, Introspection exposure, DoS attacks

## Overview (High Priority)

Due to its flexibility, GraphQL presents unique security risks: missing field-level authorization, query complexity attacks, and schema leakage.

---

## Detection Categories

### 1. Unauthorized Field Access

```graphql
# High: Sensitive fields without authorization check
type User {
  id: ID!
  username: String!
  email: String!  # Access should be restricted
  ssn: String!    # Critical: Sensitive field exposed
  salary: Float!  # High: Salary information
}

# Attack: Any user can query
query {
  users {
    id
    ssn      # Unauthorized SSN access
    salary   # Unauthorized salary access
  }
}
```

**Detection**:
```bash
# Java (graphql-java)
grep -rn "@GraphQLQuery\|@GraphQLMutation" --include="*.java" -A 10 | \
  grep -v "@PreAuthorize\|checkPermission\|hasRole"

# Schema definition
grep -rn "type User\|type Employee" --include="*.graphql" -A 20 | \
  grep "ssn\|salary\|creditCard\|password"
```

**Security Fix**:
```java
// Field-level authorization
@Component
public class UserDataFetcher {
    @PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.id")
    public String getEmail(User user, @Argument Long userId) {
        return user.getEmail();
    }

    @PreAuthorize("hasRole('HR')")
    public String getSsn(User user) {
        return user.getSsn();
    }
}

// Or use DataFetcherDirective
@GraphQLDirective(name = "auth", description = "Requires authentication")
public class AuthDirective implements SchemaDirectiveWiring {
    @Override
    public GraphQLFieldDefinition onField(SchemaDirectiveWiringEnvironment env) {
        GraphQLFieldDefinition field = env.getField();
        return field.transform(builder -> builder
            .dataFetcher(new AuthorizingDataFetcher(field.getDataFetcher()))
        );
    }
}
```

### 2. Introspection Enabled in Production

```graphql
# Medium: Introspection exposes schema in production
query {
  __schema {
    types {
      name
      fields {
        name
        type { name }
      }
    }
  }
}

# Exposes: All types, fields, parameters → Attack surface analysis
```

**Detection**:
```bash
# Spring Boot GraphQL
grep -rn "spring.graphql.schema.introspection.enabled.*true" application.yml

# graphql-java
grep -rn "GraphQL\.newGraphQL" --include="*.java" -A 10 | \
  grep -v "introspectionEnabled(false)"
```

**Security Fix**:
```yaml
# Disable introspection in production
spring:
  graphql:
    schema:
      introspection:
        enabled: false  # Production environment

# Or configure by environment
spring:
  graphql:
    schema:
      introspection:
        enabled: ${GRAPHQL_INTROSPECTION:false}
```

### 3. Batch Query DoS

```graphql
# High: Unrestricted batch queries
query {
  users {  # Returns 10000 users
    id
    posts {  # Each user has 10000 posts
      id
      comments {  # Each post has 10000 comments
        id
        author {  # Each comment has an associated user
          posts {  # Recursive...
            comments { ... }
          }
        }
      }
    }
  }
}

# Result: Database query explosion, memory exhaustion, response timeout
```

**Detection**:
```bash
# Check for query complexity limits
grep -rn "MaxQueryComplexityInstrumentation\|QueryComplexity" --include="*.java"

# Check for depth limits
grep -rn "MaxQueryDepthInstrumentation" --include="*.java"
```

**Security Fix**:
```java
// Query complexity limit
MaxQueryComplexityInstrumentation maxComplexity = new MaxQueryComplexityInstrumentation(200);

// Query depth limit
MaxQueryDepthInstrumentation maxDepth = new MaxQueryDepthInstrumentation(10);

GraphQL graphQL = GraphQL.newGraphQL(schema)
    .instrumentation(maxComplexity)
    .instrumentation(maxDepth)
    .build();

// Enforced pagination
type Query {
  users(first: Int = 10, after: String): UserConnection!  # Enforced pagination
}
```

### 4. Deep Recursion Exhaustion

```graphql
# Critical: Circular references without depth limit
type User {
  friends: [User!]!
}

query {
  user(id: 1) {
    friends {
      friends {
        friends {  # Infinite nesting
          friends { ... }
        }
      }
    }
  }
}
```

**Security Fix**:
```java
// Depth limit
MaxQueryDepthInstrumentation depthLimit = new MaxQueryDepthInstrumentation(5);

// Or custom ValidationRule
public class MaxDepthValidationRule extends AbstractRule {
    private final int maxDepth;

    @Override
    public void checkField(ValidationContext context, Field field) {
        int depth = calculateDepth(field);
        if (depth > maxDepth) {
            context.addError(new ValidationError(
                "Query depth exceeds " + maxDepth
            ));
        }
    }
}
```

### 5. IDOR via Nested Query

```graphql
# High: Nested query bypasses top-level authorization
query {
  publicPost(id: 123) {  # Public post, no authorization needed
    author {
      email        # But can access author's private information
      phone        # IDOR: Unauthorized access
      orders {     # Access author's orders
        id
        amount
      }
    }
  }
}
```

**Detection**:
```bash
# Check if nested types have independent authorization
grep -rn "type.*{" --include="*.graphql" -A 20 | \
  grep -E "email|phone|address|ssn"
```

**Security Fix**:
```java
// Independent authorization per field
public class UserDataFetcher {
    public String getEmail(User user, DataFetchingEnvironment env) {
        User currentUser = env.getContext().getCurrentUser();

        // Only self or admin can view email
        if (currentUser.getId().equals(user.getId()) ||
            currentUser.hasRole("ADMIN")) {
            return user.getEmail();
        }
        throw new UnauthorizedException("Cannot access email");
    }
}
```

---

## Comprehensive Detection Checklist

### Critical
- [ ] Sensitive fields (SSN/salary/credit card) without authorization
- [ ] Deep recursion without limits causing DoS

### High
- [ ] Batch queries without complexity/depth limits
- [ ] IDOR via nested query
- [ ] Introspection enabled in production

### Medium
- [ ] Inconsistent field-level authorization
- [ ] Pagination not enforced

---

## Minimal PoC Examples
```bash
# Introspection
curl -X POST https://api.example.com/graphql \
  -H 'Content-Type: application/json' \
  -d '{"query":"{__schema{types{name}}}"}'

# Batch DoS
curl -X POST https://api.example.com/graphql \
  -H 'Content-Type: application/json' \
  -d "{\"query\":\"{ users { posts { comments { author { posts { id } } } } } }\"}"
```

---

## False Positives

- Introspection only enabled in development environment (requires environment check)
- Public API intentionally exposes schema (requires documentation)
- Depth/complexity limits already configured

---

## References

- OWASP GraphQL Security Cheat Sheet
- GraphQL Best Practices: Authorization
- graphql-java Security
