GHSA-x9vc-9ffq-p3gj NetLicensing-MCP: Unauthenticated Use of Server-Side NetLicensing API Key in HTTP Mode
Unauthenticated Use of Server-Side NetLicensing API Key in HTTP ModeSummaryWhen `netlicensing-mcp` is run in HTTP transport mode, the `ApiKeyMiddleware` fails to enforce authentication: requests that carry no client API key are unconditionally forwarded to the next handler (`server.py:1427`). The downstream HTTP client then falls back to the server operator's `NETLICENSING_API_KEY` environment variable (`client.py:30`) and uses it to authenticate every upstream call to the NetLicensing REST API. An unauthenticated network attacker can therefore invoke any MCP tool — including product listing, license creation/modification, and destructive delete operations — entirely under the operator's identity and account quota. CVSS 3.1 Base Score: **8.1 (High)**.DetailsThe HTTP transport is started in `src/netlicensing_mcp/server.py` around line 1430 via `mcp.streamable_http_app()`, and `ApiKeyMiddleware` is registered immediately after (line 1431). The middleware implementation (lines 1412–1427) attempts to extract a per-request API key from either the `x-netlicensing-api-key` header or the `?apikey=` query parameter. However, if neither source provides a key, the middleware takes no enforcement action and simply calls `return await call_next(request)` (line 1427), passing the unauthenticated request downstream.The downstream client module (`src/netlicensing_mcp/client.py`) uses a Python `ContextVar` named `api_key_ctx` with a default of `os.getenv("NETLICENSING_API_KEY", "")` (line 30). Because the middleware never sets this context variable for unauthenticated requests, `api_key_ctx.get()` returns the server-level environment variable. The client then encodes this value into an HTTP Basic Authorization header (`lines 62–70`) and transmits it to the upstream NetLicensing REST API on every request (`lines 105, 109`).The complete exploitable data flow is:| Step | Location | Description |
|------|----------|-------------|
| 1 | `server.py:1430` | HTTP app created with `mcp.streamable_http_app()` |
| 2 | `server.py:1431` | `ApiKeyMiddleware` registered |
| 3 | `server.py:1412–1419` | Middleware attempts (optional) key extraction from headers/query |
| 4 | `server.py:1427` | **Auth bypass sink**: missing key → `return await call_next(request)` |
| 5 | `server.py:155–163` | Unauthenticated caller invokes `netlicensing_list_products` (or any tool) |
| 6 | `tools/products.py:9,17` | Tool delegates to `nl_get("/product", ...)` |
| 7 | `client.py:30` | **Source**: `api_key_ctx` defaults to `NETLICENSING_API_KEY` env var |
| 8 | `client.py:62–70` | `Authorization: Basic base64("apiKey:<key>")` constructed |
| 9 | `client.py:105,109` | **Upstream sink**: `client.get(url, headers=_headers(), ...)` executed |Critical code excerpts:The README (`README.md:90–94`) documents the HTTP mode deployment pattern with `-e NETLICENSING_API_KEY=your_key` as a first-class production deployment option, including AWS App Runner / ELB examples (`README.md:310–318`). The per-client key recommendation (`README.md:318`) is advisory only and is not technically enforced.A suggested patch replaces the unconditional pass-through with a `401` rejection:PoC**Environment requirements:**Docker (or Python 3.12 with `netlicensing-mcp==0.1.5` and `mcp` client installed)Target commit: `ef0080c2aebbf4dfbce93a959dd7c1471103c05a`**Self-contained Docker reproduction (all-in-one):****Manual step-by-step reproduction:****Expected output in Terminal 1:**Decoding the Base64 credential confirms the operator's secret was used:**Observed evidence from dynamic reproduction (Phase 2):**ImpactThis is a **Missing Authentication for Critical Function (CWE-306)** vulnerability. Any network-reachable attacker who can send HTTP requests to the `/mcp` endpoint can invoke the full set of MCP tools — including read, create, update, and delete operations — without supplying any credential. The attacker's requests are transparently executed under the server operator's NetLicensing account.Concrete consequences include:**Confidentiality**: enumeration of all products, licenses, licensees, and transactions associated with the operator's account.**Integrity**: creation of new licenses or licensees, modification of existing license parameters, and forging token-based validations.**Availability**: bulk deletion of products, licenses, or licensees, destroying the operator's licensing configuration.**Who is impacted**: Operators who deploy `netlicensing-mcp` in HTTP transport mode (`python3 -m netlicensing_mcp.server http`) with `NETLICENSING_API_KEY` set as a server-side environment variable and expose the service on a network-reachable interface. This deployment pattern is officially documented in the project README for remote/shared and cloud deployments.Reproduction artifacts`Dockerfile``poc.py`
CVSS v3.1 score: 8.1