fix(register): allowlist headers persisted at client registration#264
Open
sebastiondev wants to merge 1 commit into
Open
fix(register): allowlist headers persisted at client registration#264sebastiondev wants to merge 1 commit into
sebastiondev wants to merge 1 commit into
Conversation
Previously the dynamic client registration handler stored every request header on the new client record. The handler iterated over request.headers and persisted them verbatim via model.saveClientRegisterHeaders, which meant that headers like Authorization, Cookie, and infrastructure-injected headers (X-Forwarded-For, internal trace IDs, etc.) were saved into the KV store and later re-read in the authorize flow. The authorize handler only consults a small set of x-read-only / x-neon-* headers, so restrict the persisted headers to that allowlist. Adds a regression test that asserts sensitive headers are not stored.
|
@sebastiondev is attempting to deploy a commit to the neondatabase Team on Vercel. A member of the Team first needs to authorize it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Vulnerability summary
The
POST /api/registerendpoint inlanding/app/api/register/route.tsiterates over all incoming request headers and persists them into the KV store viamodel.saveClientRegisterHeaders(). This endpoint is unauthenticated — it implements the OAuth 2.0 Dynamic Client Registration flow — and is reachable on the public internet atmcp.neon.tech.Because every header is stored, sensitive values end up persisted:
Authorization— bearer tokens from upstream proxies or mistaken client configurationsCookie— session cookies attached by browsers or intermediariesX-Forwarded-For/X-Forwarded-Proto/X-Real-IP— internal infrastructure topologyThese stored headers are later read back in the
/authorizeflow, meaning the leaked data could also be reflected to other parties.CWE-200: Exposure of Sensitive Information to an Unauthorized Actor
Affected file:
landing/app/api/register/route.ts, lines 14–17 (therequest.headers.forEachloop)Data flow:
request.headers→requestHeadersobject (all keys) →model.saveClientRegisterHeaders(clientId, requestHeaders)→ Postgres-backed KV store → read back in/authorizeProof of Concept
All five custom headers above would be persisted into the KV store. Only
x-read-onlyis actually consumed by the authorize handler — the rest are sensitive data leaking into persistent storage.Fix description
The fix introduces a strict allowlist of headers that may be persisted:
The
forEachloop now checksALLOWED_REGISTER_HEADERS.includes(lower)before adding a header to therequestHeadersobject. This ensures only the four headers that the authorize flow actually consumes are stored — everything else is silently dropped.This is a minimal, behavior-preserving change: the authorize handler only reads
x-read-only(and potentially the otherx-neon-*headers), so no legitimate functionality is affected.Test results
A new integration test (
does not persist sensitive or unknown headers) was added tolanding/mcp-src/__tests__/oauth-register-route.integration.test.ts. The test sends a registration request with both allowed (x-read-only: true) and sensitive (authorization,cookie,x-forwarded-for,x-internal-trace) headers, then asserts:{ 'x-read-only': 'true' }is passed tosaveClientRegisterHeadersclient_id)All existing tests continue to pass — the fix does not change behavior for legitimate registrations.
Security analysis
This is exploitable by any unauthenticated network client. The
/api/registerendpoint requires no authentication (by design — it implements RFC 7591 Dynamic Client Registration). An attacker can craft requests with arbitrary headers, and all of them are written to persistent storage. The risk compounds because:Before submitting, we verified that no existing middleware or framework protection filters headers before they reach the route handler — Next.js App Router passes the full
NextRequestheaders object through unchanged, and there is no header-stripping middleware in the Vercel deployment configuration.Submitted by Sebastion — autonomous open-source security research from Foundation Machines. Free for public repos via the Sebastion AI GitHub App.