Skip to content

Uses Public Default Authentication Secrets #591

Description

@28Hus

Self Checks

  • I have read the Contributing Guide.
  • I have searched for existing issues search for existing issues, including closed ones.
  • I can provide the report in English if needed to help more contributors participate in the discussion.
  • Please do not modify this template :) and fill in all the required fields.

Aitoearn version

Commit d3aa8be and earlier versions

Please select your platform

Mac

Steps to reproduce

AiToEarn Uses Public Default Authentication Secrets

Package

AiToEarn

Affected versions

Commit d3aa8bea5b146a8675607cf0144d891aad3e9683 and earlier versions
containing the affected code.

Patched versions

Unknown.

Description

Summary

AiToEarn contains public default values for authentication secrets. The
Electron server uses a static fallback for AUTH_SECRET to sign and verify
JWTs. The JWT payload contains isManager, and ManagerGuard grants manager
access when this claim is true.

The backend configuration also contains a static internalToken. The shared
authentication guard accepts this value directly as a Bearer token without
JWT verification or user lookup. Internal controllers protected by
@Internal() therefore rely on this publicly known value as their only
authentication factor.

Details

Component File Issue
Electron server project/aitoearn-electron/server/src/auth/auth.module.ts Static fallback for AUTH_SECRET
Electron server project/aitoearn-electron/server/src/auth/manager.guard.ts Trusts isManager after JWT verification
Backend services project/aitoearn-backend/apps/aitoearn-server/config/config.yaml Static auth.secret and auth.internalToken defaults
Shared backend auth project/aitoearn-backend/libs/aitoearn-auth/src/aitoearn-auth.guard.ts Accepts internalToken directly

Vulnerable Code

project/aitoearn-electron/server/src/auth/auth.module.ts:

JwtModule.register({
  global: true,
  secret: process.env.AUTH_SECRET || "<public static default redacted>",
  signOptions: { expiresIn: '30d' },
})

The application places the manager flag inside the JWT:

const payload: TokenInfo = {
  phone: tokenInfo.phone,
  id: tokenInfo.id,
  name: tokenInfo.name,
  isManager: tokenInfo.isManager,
};
return this.jwtService.sign(payload);

ManagerGuard then accepts the authorization decision from the token:

const payload = await this.jwtService.verifyAsync(token, {
  secret: process.env.AUTH_SECRET,
});

if (!payload.isManager) {
  throw new UnauthorizedException();
}

The backend configuration also publishes static authentication values:

auth:
  secret: <public static default redacted>
  internalToken: <public static default redacted>

The shared guard accepts the internal token without establishing a user:

if (token === this.options.internalToken) {
  return true;
}

PoC

Run only against a local test deployment.

JWT manager authorization

  1. Leave AUTH_SECRET unset in the Electron server.
  2. Create a JWT with the public fallback key and a payload containing
    isManager: true.
  3. Send the token as Authorization: Bearer <token> to a route protected by
    @Manager(), such as the manager creation route.
  4. Repeat the test with a unique random AUTH_SECRET.

Example token-generation pseudocode:

const token = jwt.sign(
  { id: '<test-id>', isManager: true },
  '<public static default redacted>',
  { expiresIn: '30d' },
);

Expected secure result: the forged token is rejected. The affected result is
that the request passes ManagerGuard and reaches the manager-only handler.

Internal bearer token

  1. Start the backend with the checked-in internalToken value unchanged.
  2. Send that value as a Bearer token to an endpoint protected by @Internal().
  3. For example, test the AI draft-generation internal endpoints using a body
    that satisfies the endpoint DTO.
  4. Repeat with a newly generated internal token.

Expected secure result: an external request without an explicitly authorized
service credential is rejected. The affected result is that the static token
passes the guard without JWT verification, API-key resolution, or user lookup.

Attack Scenario

  • An attacker reads the public repository and obtains the default values.
  • The deployment does not override the corresponding environment/configuration
    values.
  • The attacker signs a JWT with isManager: true, or sends the default
    internalToken as a Bearer token.
  • The server accepts the token as an authenticated manager or internal caller.

Impact

The Electron JWT fallback may allow unauthorized access to manager-only
handlers, including manager administration operations. The backend
internalToken may allow unauthorized access to internal service endpoints,
including operations that accept a caller-supplied userId.

The backend auth.secret path additionally uses the token id to resolve an
open user record. Its exact impact should be verified against the deployed
token-issuing path, but the checked-in value is not suitable as a production
JWT secret.

Recommended Fix

  1. Remove all static authentication fallbacks from source-controlled
    configuration.
  2. Fail closed when AUTH_SECRET, auth.secret, or internalToken is absent
    or unchanged from a development value.
  3. Generate instance-specific cryptographically random secrets during secure
    deployment and store them outside the repository.
  4. Replace the exposed values and invalidate tokens issued with them.
  5. Do not use a bearer string as an implicit internal identity; authenticate
    service callers with scoped, rotatable credentials and enforce service-side
    authorization.
  6. Add regression tests proving that default or missing secrets cannot start a
    production deployment and that forged manager/internal requests are
    rejected.

References

✔️ Expected Behavior

...

❌ Actual Behavior

...

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions