Built-in ID scalar used in input instead of a custom scalar

Issue ID: graphql-data-input-custom-scalar-id-needed

Average severity: Critical

Description

The schema uses a built-in scalar of the type ID in an input position (argument or input object field) instead of using a domain-specific custom scalar.

For more details, see the GraphQL specification.

Possible exploit scenario

In GraphQL, the built-in ID scalar is defined as a unique identifier serialized as a string. However, this poses several problems:

  • It accepts any string or integer value
  • It has no format or length constraints
  • It carries no semantic meaning
  • It does not express ownership, tenant scope, or domain boundaries
  • It does not enforce structural validation

In practice, identifiers in GraphQL APIs are frequently used to:

  • Reference business entities (userId, accountId, orderId)
  • Control ownership (tenantId, organizationId)
  • Modify or delete objects
  • Establish relationships between entities

Because identifiers are often directly tied to authorization decisions, using an unconstrained built-in ID in input position increases the risk of:

  • Broken Object Level Authorization (BOLA)
  • Cross-tenant access
  • Insecure direct object reference patterns
  • Type confusion between identifiers belonging to different domains

For example, if both UserId and AccountId are defined as ID, the schema does not prevent accidentally passing a valid account identifier where a user identifier is expected. The backend must rely entirely on runtime checks, increasing the risk of implementation errors.

A custom scalar makes the domain semantics explicit and allows enforcing:

  • Strict format (UUID, ULID, base64-encoded opaque ID)
  • Length constraints
  • Namespace or prefix rules
  • Structural validation before resolver execution

This improves both security posture and contract clarity.

Remediation

Replace the built-in ID scalar with a domain-specific custom scalar that carries explicit constraints. We recommend that you:

  • Define a distinct scalar for each identifier domain, such as:
    • UserId
    • AccountId
    • OrderId
    • TenantId
  • Enforce strict format and length constraints
  • Avoid reusing the generic ID for security-sensitive references

This reduces ambiguity, strengthens validation at the API boundary, and supports more accurate security auditing and authorization testing.