VulnWatch VulnWatch
← Back to dashboard
High github · GHSA-x9vc-9ffq-p3gj

NetLicensing-MCP: Unauthenticated Use of Server-Side NetLicensing API Key in HTTP Mode

Published Jul 14, 2026 CVSS 8.1

Unauthenticated Use of Server-Side NetLicensing API Key in HTTP Mode

Summary

When 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).

Details

The 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:") constructed
9 client.py:105,109 Upstream sink: client.get(url, headers=_headers(), ...) executed

Critical code excerpts:

# src/netlicensing_mcp/server.py
1418:     if not key:
1419:         key = request.query_params.get("apikey")
1421:     if key:
1422:         token = api_key_ctx.set(key)
            ...
1427:     return await call_next(request)   #

Affected AI Products

mcp server
Get the weekly digest. Every Monday: top AI security stories of the week. Free.