fix(security): tighten /_proxy/mode origin check (CSRF / DNS rebinding)#16
Open
aaronjmars wants to merge 1 commit into
Open
fix(security): tighten /_proxy/mode origin check (CSRF / DNS rebinding)#16aaronjmars wants to merge 1 commit into
aaronjmars wants to merge 1 commit into
Conversation
The origin allowlist on the /_proxy/mode endpoint used String.startsWith to match `http://127.0.0.1` and `http://localhost`. Because startsWith has no boundary, an Origin like `http://localhost.attacker.com:3200` or `http://127.0.0.1.attacker.com:3200` also passed. Combined with DNS rebinding (attacker registers `localhost.attacker.com`, serves a page from port 3200, then rebinds the A record to 127.0.0.1), a victim browsing the attacker page can POST to /_proxy/mode and silently switch the local proxy's backend. Impact: - Prompts intended for one provider are silently routed to another (e.g., user thinks Anthropic, attacker switches to DeepSeek → proprietary code/secrets leak to a different vendor). - Cost: forcing a switch from DeepSeek to Anthropic is a 17x bill spike. - The proxy attaches the user's stored API key for the active backend, so the switched-in vendor receives valid auth. Replace the prefix check with a strict regex that anchors the host exactly (127.0.0.1, localhost, or [::1]) with optional port. Curl/CLI callers that omit the Origin header continue to work unchanged. Detected by Aeon (manual review of proxy/model-proxy.js). Severity: medium. CWE-352 (CSRF) + CWE-350 (reverse-DNS reliance).
3 tasks
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.
Summary
proxy/model-proxy.jsexposes a control endpoint atPOST /_proxy/modethat swaps the local proxy's active backend (DeepSeek / OpenRouter / Fireworks / Anthropic). The CSRF defense is an Origin-header allowlist:String.prototype.startsWithhas no boundary, sohttp://localhost.attacker.com:3200andhttp://127.0.0.1.attacker.com:3200both pass the check. Combined with DNS rebinding — attacker registers a domain likelocalhost.attacker.com, hosts the page on port 3200, then re-points the A record to 127.0.0.1 — a victim browsing the attacker page canfetch('/_proxy/mode', {method:'POST', body:'backend=...'})and silently switch the user's active backend.Impact
/v1/messages. After a forced switch, prompts the user composes against (e.g.) Anthropic are sent to DeepSeek with valid auth — proprietary code, secrets in scrollback, and conversation context follow.Location
proxy/model-proxy.js:230-235(againstmain@ 70518b6).Fix
Replace the prefix check with a strict regex anchored to the loopback host with optional port:
Curl/CLI usage (no Origin header) continues to work, matching the documented usage in the README.
Verification
End-to-end against a local instance of
startModelProxyon port 13200:http://localhost:13200http://127.0.0.1http://localhost.attacker.com:13200http://127.0.0.1.attacker.com:13200http://attacker.comPreserves all legitimate paths (curl, slash-command flow, VS Code task POSTs from loopback) and closes both DNS-rebinding bypass shapes.
Detected by
Aeon (manual review of
proxy/model-proxy.jsafter Semgrep / TruffleHog / OSV came back clean).Filed by Aeon. Close it if you'd rather fix this differently.