Internal task headers can forge user sessions with missing or public default token
Internal task headers can forge user sessions with missing or public default task token
Summary
The internal task authentication path trusts request headers to construct an authenticated user session. A request containing x-internal-user-id is accepted as that user when either:
INTERNAL_TASK_TOKENis unset andNODE_ENVis notproduction; orINTERNAL_TASK_TOKENis set to a value known by the attacker.
The second condition is relevant to default deployments because the repository ships public default/example token values in docker-compose.yml and .env.example. If those values are not changed, an external caller can provide the known x-internal-task-token plus an arbitrary x-internal-user-id and be treated as that user by normal API routes.
Affected code
src/lib/api-auth.tsgetInternalTaskSession()readsx-internal-task-tokenandx-internal-user-id.- If the expected token matches, it returns an
AuthSessionwithsession.user.id = x-internal-user-id. - If no expected token is configured, non-production environments accept the header without a token.
getAuthSession()returns the internal task session before falling back to NextAuth.requireUserAuth(),requireProjectAuth(), andrequireProjectAuthLight()trust the returnedsession.user.idfor user and project authorization.
Impact
An unauthenticated network attacker can impersonate a known user ID when the task token is missing or left at the public default/example value. This allows access to ordinary authenticated API routes as that user.
Representative impact includes:
- reading user balance/cost information;
- listing user projects and project previews;
- reading or modifying project data and asset-library data;
- triggering generation/task APIs as the impersonated user, which may consume resources or alter user-owned data.
This is an authentication bypass / user impersonation issue. It does not appear to provide direct remote code execution.
Reproduction
Prerequisites:
- the app is reachable over the network;
- the target user's UUID is known; and
- either:
INTERNAL_TASK_TOKENis unset whileNODE_ENVis notproduction; orINTERNAL_TASK_TOKENis still the public default/example value from the repository.
Example using the public Docker default token:
curl -i "http://HOST:PORT/api/projects" \
-H "x-internal-user-id: <victim-user-uuid>" \
-H "x-internal-task-token: waoowaoo-docker-task-token"Example for non-production with no task token configured:
curl -i "http://HOST:PORT/api/projects" \
-H "x-internal-user-id: <victim-user-uuid>"Expected behavior:
- The request should be rejected unless it has a real authenticated user session or a strongly authenticated internal-worker identity that cannot be supplied by external clients.
Actual behavior:
- The request is accepted as
session.user.id = <victim-user-uuid>and downstream authorization checks use that forged user ID.
Root cause
The session identity key exposed to route authorization is only userId. Security-relevant proof fields such as token presence, token secrecy, deployment environment, and internal-call provenance are not preserved or revalidated by callers. Public default token values also make token-based protection ineffective for unchanged default deployments.
Suggested remediation
- Require
INTERNAL_TASK_TOKENto be present and high entropy in all environments where internal task headers are accepted. - Refuse startup when the token is empty or equal to a known default/example value.
- Do not treat
x-internal-user-idas a normal user session. Separate internal-worker identity from end-user identity and authorize internal task calls explicitly. - Strip or reject externally supplied
x-internal-*headers at the reverse proxy / middleware boundary unless the request comes from a trusted internal source. - Rotate the default/example tokens and document that deployments must generate unique secrets.
Severity
High to Critical, depending on deployment:
- Critical for public deployments using unchanged default/example task tokens or exposed non-production environments with no token.
- Lower for strictly local-only deployments or production deployments with a private high-entropy task token and external header stripping.
Source: waooAI/waoowaoo