Skip to content

Admin-key comparison uses plain === instead of the codebase's own timing-safe compare, duplicated in two files #526

Description

@sshdopey

Where: src/middleware/apiKeyAuth.ts line ~25, and
src/graphql/schema.ts createGraphQLContext() line ~87.

What's wrong: both files independently implement the same "is this the
admin key" check:

const adminKey = process.env.ADMIN_API_KEY;
if (adminKey && providedKey === adminKey) { ... }

This is a plain, non-constant-time string comparison. The codebase already
has src/lib/timing-safe.ts (timingSafeCompare) specifically to avoid this
class of bug, and src/routes/admin.ts correctly uses it for its own admin
bearer-token check:

if (!timingSafeCompare(authorization, `Bearer ${apiKey}`)) { ... }

but that fix was never applied to apiKeyAuth.ts or the GraphQL context
builder, which duplicate the same admin-key check logic independently (see
companion issue about apiKeyAuth.ts being unused — the GraphQL copy is
live, since createGraphQLContext is wired into the /graphql endpoint).

Impact: the /graphql endpoint's admin check (isAdmin) is vulnerable to
a timing side-channel on ADMIN_API_KEY, the same class of issue previously
fixed in routes/admin.ts (issue #252 "Use timing-safe comparison for Bearer
token validation") but not applied consistently across all three places this
comparison now exists.

Suggested fix: replace both occurrences with timingSafeCompare (or
better, extract the "resolve isAdmin/isConsumer from headers" logic into one
shared helper used by both the middleware and the GraphQL context, since it's
currently copy-pasted almost verbatim between the two files).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Stellar WaveIssues in the Stellar wave program

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions