Skip to content
IBMGHSA-8pcq-mx48-hjvj

Default JWT Secret in Docker Compose Enables Forged Platform-Admin Tokens

Critical9.8Published Aug 25, 2026

## Default JWT Secret in Docker Compose Enables Forged Platform-Admin Tokens ### Summary IBM mcp-context-forge (mcpgateway) ships with a publicly known HMAC JWT signing secret (`my-test-key-but-now-longer-than-32-bytes`) as the default fallback value in `docker-compose.yml` and `docker-compose-verbose-logging.yml`. Because the default deployment also sets `REQUIRE_USER_IN_DB=false` and `ENVIRONMENT=development`, a remote unauthenticated attacker can craft a valid HS256 JWT bearing `sub=admin@example.com`, `is_admin=true`, and `teams=null`, present it to any bearer-auth–protected endpoint, and obtain full platform-administrator access with no credentials required. The vulnerability is trivially exploitable against any deployment that uses the documented Docker Compose quickstart without explicitly overriding `JWT_SECRET_KEY`. CVSS Base Score: **9.8 (Critical)**. --- ### Details **Root cause — publicly known default secret** `docker-compose.yml` line 355 defines the JWT signing key using a shell-parameter-expansion fallback: ```yaml # docker-compose.yml:355 JWT_SECRET_KEY: ${JWT_SECRET_KEY:-my-test-key-but-now-longer-than-32-bytes} ``` If an operator runs `docker-compose up` ...

GitHub advisory

Affected versions

PackageAffectedFixed in
mcp-contextforge-gateway
PyPI
< v1.0.7v1.0.7
Details and references

## Default JWT Secret in Docker Compose Enables Forged Platform-Admin Tokens ### Summary IBM mcp-context-forge (mcpgateway) ships with a publicly known HMAC JWT signing secret (`my-test-key-but-now-longer-than-32-bytes`) as the default fallback value in `docker-compose.yml` and `docker-compose-verbose-logging.yml`. Because the default deployment also sets `REQUIRE_USER_IN_DB=false` and `ENVIRONMENT=development`, a remote unauthenticated attacker can craft a valid HS256 JWT bearing `sub=admin@example.com`, `is_admin=true`, and `teams=null`, present it to any bearer-auth–protected endpoint, and obtain full platform-administrator access with no credentials required. The vulnerability is trivially exploitable against any deployment that uses the documented Docker Compose quickstart without explicitly overriding `JWT_SECRET_KEY`. CVSS Base Score: **9.8 (Critical)**. --- ### Details **Root cause — publicly known default secret** `docker-compose.yml` line 355 defines the JWT signing key using a shell-parameter-expansion fallback: ```yaml # docker-compose.yml:355 JWT_SECRET_KEY: ${JWT_SECRET_KEY:-my-test-key-but-now-longer-than-32-bytes} ``` If an operator runs `docker-compose up` without setting `JWT_SECRET_KEY` in the shell environment or an `.env` file, the literal string `my-test-key-but-now-longer-than-32-bytes` is used as the HMAC-SHA256 signing key. The same value is hardcoded without any substitution in `docker-compose-verbose-logging.yml:223`: ```yaml # docker-compose-verbose-logging.yml:223 JWT_SECRET_KEY=my-test-key-but-now-longer-than-32-bytes ``` The Python-level application default in `mcpgateway/config.py:396` is `"changeme"`, but the Docker Compose files override this with the above well-known string. The secret is explicitly listed in `mcpgateway/_security_constants.py:9-10` under `WEAK_VALUES`, confirming the vendor was aware of its weakness. However, when `ENVIRONMENT=development` (the Docker Compose default, `docker-compose.yml:368`), the startup check only emits a warning (`🔓 SECURITY WARNING - jwt_secret_key: Default/weak secret detected!`) and does **not** abort startup. **Attack path** The full exploitation chain proceeds as follows: 1. **Secret exposure** — `docker-compose.yml:355` sets `JWT_SECRET_KEY` to the public value when no override is provided. 2. **Token verification** — `mcpgateway/utils/verify_credentials.py:302–314` validates incoming JWTs with the configured key and algorithm (`HS256`). An attacker who knows the key can produce a valid signature. 3. **Bearer auth acceptance** — `mcpgateway/auth.py:1426–1428` calls `verify_jwt_token_cached`; any correctly signed token passes. 4. **RBAC bypass** — `mcpgateway/auth.py:539–575` (`normalize_token_teams`): when `teams=null` and `is_admin=true`, the function returns `None`, which is treated as unrestricted admin scope, bypassing all RBAC checks. 5. **Virtual admin bootstrap** — `mcpgateway/auth.py:1869–1878`: when `REQUIRE_USER_IN_DB=false` (default in Docker Compose, `docker-compose.yml:422`) and the token's `sub` matches `PLATFORM_ADMIN_EMAIL` (`admin@example.com`), the server synthesizes a virtual admin user object without any database lookup. 6. **Platform-admin email bypass** — `mcpgateway/services/permission_service.py:131–133` → `mcpgateway/utils/admin_check.py:84–86`: if `user_email == settings.platform_admin_email`, all permission checks return `True`. 7. **Protected route access** — `mcpgateway/main.py:6842–6849`: the `GET /gateways` route (and all other `@require_permission`-decorated routes) becomes fully accessible to the forged token. **Server startup warning (not a blocker)** The server logs the following at startup but continues to accept connections: ``` 🔓 SECURITY WARNING - jwt_secret_key: Default/weak secret detected! Using potentially ephemeral storage with platform admin bootstrap enabled ``` --- ### PoC **Environment setup** Save the following as `Docker

CVSS 3.1
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Severity from
GitHub (reviewed advisory)
Weakness
CWE-798

More IBM advisories

All IBM

Critical advisories by email

Wednesdays: the week’s critical and high advisories in the AI and data stack, with the fixed versions. Only in weeks that have some.

Double opt-in. Unsubscribe any time.