Skip to content

Pre-tunnel security checklist for the OAuth gateway (blocks the tunnel phase) #404

Description

@kengio

From PR #403's final adversarial security review. Each item is safe-at-loopback but MUST land before the gateway is exposed through a tunnel.

  1. Host/Origin validation on the OAuth routes. /mcp inherits rmcp's DNS-rebinding guard (allowed_hosts = ["localhost","127.0.0.1","::1"]), but the merged /register, /authorize, /token routers get nothing — a rebinding page can reach them same-origin and read the responses. Add a guard + a test pinning the behavior.
  2. StreamableHttpServerConfig::allowed_hosts must include the tunnel hostname. rmcp's default is loopback-only, so /mcp will reject every tunnelled request until this is set. Functional landmine, not just security — the tunnel simply won't work without it.
  3. Registration caps / rate limiting on /register. Unauthenticated remote write amplification: clients.json is fully re-serialized per write (O(N²) disk) and re-parsed under the global mutex on every later request, so a bloated store degrades /mcp too. PR feat(gateway): OAuth 2.1 authorization + resource server with pairing (Gateway PR 3) #403 added a per-registration redirect_uris cap and wired purge_expired; a client-count cap and a rate limit are still open.
  4. public_url hardening beyond PR feat(gateway): OAuth 2.1 authorization + resource server with pairing (Gateway PR 3) #403's validation — re-review once a real tunnel URL is in play (path-prefixed deployments also break render_consent_form's root-relative action="/authorize").
  5. Strengthen the consent page's identity signal. It currently shows only the redirect host; show the full escaped redirect_uri and label client_name as self-asserted. Attacker-controlled text in an <h1> is the whole basis of the user's trust decision.
  6. Decide the CSRF posture explicitly. There is no CSRF token; the pairing code in the POST body is what defeats a blind cross-site auto-submit. That works, but it is a static, long-lived, reused secret. Add Origin/Sec-Fetch-Site checking on POST /authorize and/or rotate the pairing code after a successful authorization.
  7. Document the global-lockout DoS tradeoff. Post-tunnel, anyone holding a client_id can keep the owner permanently locked out at 5 failures/60s. The global (not per-IP) lockout is the right shape for a single-user gateway, but the tradeoff should be written down.

Also worth folding in: RFC 8707 resource is validated but never bound onto TokenRecord (harmless with one resource, a latent trap with two); token_refresh_grant doesn't check client_id against the token's record (OAuth 2.1 §4.3.1 conformance, no escalation possible); no IPv6 loopback (http://[::1]) in is_loopback_redirect_uri (RFC 8252 §7.3 interop).

🤖 Generated with Claude Code

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions