Rightshold™ Sign up

Evidence Delivery · $99 / 30 days · Offers open November 18, 2026 · online purchasing currently unavailable

Answers · MCP security

Our MCP endpoint only accepts calls from a short list of IP addresses, as a second layer on top of tokens. Could a caller from anywhere get past that list just by sending an X-Forwarded-For header with an allowed address, and how do we check that our allowlist, and any API key setting, is actually enforced rather than only configured?

Last updated

TL;DR: Yes, if the server reads the client address from X-Forwarded-For without checking who added it. GHSA-wchh-mrv2-5h4q (CVE-2026-86041, added to GitHub's advisory database November 9, 2026) shows it in ezBookkeeping: its MCP and API-token IP allowlists (mcp_allowed_remote_ips, api_token_allowed_remote_ips) matched against Gin's ClientIP(), and because the router never called SetTrustedProxies, Gin trusted every peer's forwarding headers. One header, X-Forwarded-For: <an allowed IP>, got a request from any address through the gate, and the same forged address went into the access log and each transaction's CreatedIp, so the records pointed at the trusted host. Tokens were still required; the advisory says the bypass removes the compensating IP control, not the token check. ezBookkeeping 1.6.0 accepts X-Forwarded-For, X-Real-IP and X-Real-Port only from configured trusted proxies, or from the private ranges when none are configured. The rule from MDN's header reference: use only addresses your own proxies added, read from the right, and if the server can be reached directly from the internet, trust no part of the header. A related lesson came on October 29, 2026: @sylphx/pdf-reader-mcp logged "API key authentication enabled" while never checking the key (CVE-2026-62264), so test every control from outside instead of trusting the config or the log line.

What should I check, step by step?

1. Find where your server gets the client address for allowlists, rate limits and logs. If it reads X-Forwarded-For, X-Real-IP or Forwarded, find out which peers it trusts to send them.

  1. In Gin, call SetTrustedProxies with your proxy's addresses, or SetTrustedProxies(nil) if there is no proxy. In other frameworks, find the equivalent trust-proxy setting and set it to your actual proxy hops, not to everything.
  2. Pick the client address from the right of the X-Forwarded-For list, skipping only your own proxies' addresses, and treat several X-Forwarded-For headers as one combined list.
  3. Make the server reachable only through the proxy: firewall the port or bind it to the private interface the proxy uses. If a client can connect directly, its own headers are untrusted.
  4. If you rely on a trusted private range, remember that other hosts and containers on that network can send the header too; narrow it to the proxy's address where you can.
  5. Log the connecting peer address next to the derived client address, so a forged header shows up as a mismatch instead of rewriting your audit trail.
  6. Treat an IP allowlist as a second layer only. Keep token or OAuth checks on every call, because an address can be shared, reassigned or forged.
  7. Test it from outside: call the endpoint from an address that isn't on the list, with and without X-Forwarded-For set to an allowed address, and with no credential. Expect a refusal every time.

How did a spoofed X-Forwarded-For header get past ezBookkeeping's MCP IP allowlist (CVE-2026-86041)?

ezBookkeeping, a self-hosted personal finance app with a built-in MCP endpoint, lets operators limit its MCP endpoint and API tokens to listed source addresses. The advisory says the web server built its Gin router with gin.New() and never called SetTrustedProxies, so Gin treated every peer as a trusted proxy and ClientIP() returned whatever X-Forwarded-For said. The allowlist middleware compared that value with the operator's list.

Which X-Forwarded-For address can an MCP server trust for IP allowlists and rate limits?

MDN's X-Forwarded-For reference states the rule. Each proxy appends the address it received the request from, so only the entries added by proxies you control are trustworthy; anything to their left can be spoofed. Security uses such as rate limiting and IP-based access control must only use addresses added by a trusted proxy, and if the server can be reached directly from the internet, even when it also sits behind a trusted proxy, no part of the list can be trusted.

How do you check that an MCP server's API key or IP allowlist is actually enforced?

A configured setting isn't proof the request path checks it. In @sylphx/pdf-reader-mcp (GHSA-q344-5v34-gm84, CVE-2026-62264, high, CVSS 3.1 7.5, added to GitHub's advisory database October 29, 2026), HTTP mode read MCP_API_KEY and logged "API key authentication enabled (X-API-Key header)" at startup, but never validated the header. With HTTP mode bound to 0.0.0.0, any client that could reach the port could call every PDF tool and read any PDF the process could open. Version 3.0.1 checks the key with a constant-time comparison, answers 401 when it is missing or wrong, binds to 127.0.0.1 and warns when a non-loopback host has no key.

How do I ask Rightshold about this (free, no key)?

Add the MCP server https://www.rightshold.com/mcp to Claude, Cursor, ChatGPT, Grok or VS Code, then ask: “We set trusted proxies on our MCP server and re-ran the outside probes: a non-allowlisted address with X-Forwarded-For set to an allowed IP, and a call with no API key, were both refused. Record the change and the probe results so we have a dated record outside our own logs.”

One-click setup

{
  "tool": "record_action",
  "arguments": {
    "action_type": "security.config_change",
    "outcome": "success",
    "occurred_at": "2026-11-13T23:30:00Z",
    "client_event_id": "mcp-allowlist:trusted-proxies-2026-11-13",
    "summary": "MCP endpoint: trusted proxies set to the load balancer's address only; client IP taken from the rightmost untrusted X-Forwarded-For entry; port reachable only from the load balancer. Probes from an outside address: spoofed X-Forwarded-For with an allowlisted IP refused 403; no API key refused 401",
    "action_sha256": "<sha256 of your probe script output>"
  }
}

Rightshold isn't affiliated with ezBookkeeping, Gin, Sylphx, MDN or the Model Context Protocol project, and it doesn't scan servers, test allowlists or inspect proxy settings. Advisory details are as published in GitHub's advisory database and the mayswind/ezbookkeeping and SylphxAI/pdf-reader-mcp repositories, the 1.6.0 change as listed in ezBookkeeping's release notes, Gin's proxy guidance as written in its docs/doc.md, and the header rules from MDN's X-Forwarded-For reference, all read November 13, 2026; advisories get updated, so confirm fixed versions on the linked pages.

What are the sources?

Related

Not legal advice. JSON · Markdown · All answers

Get a free API key in one call

No card and no email needed. 100 free credits; the license and policy checks stay free without a key.

curl -s -X POST https://www.rightshold.com/v1/accounts/signup -H "Content-Type: application/json" -d '{"accept_terms":true}'
export RIGHTSHOLD_API_KEY=rs_...   # the api_key from the reply (shown once)
curl -s https://www.rightshold.com/v1/licenses/check -H "Authorization: Bearer $RIGHTSHOLD_API_KEY" \
  -H "Content-Type: application/json" -d '{"license":"MIT","intended_use":{"commercial_use":true}}'