Skip to content

fix(proxy): proxy.php refuses to run open, constant-length token compare, negative max_tokens - #1

Merged
dboudreau00 merged 2 commits into
mainfrom
fix/proxy-hardening
Oct 21, 2026
Merged

dboudreau00 merged 2 commits into
mainfrom
fix/proxy-hardening

Conversation

@dboudreau00

Copy link
Copy Markdown
Owner

What

proxy.php fails closed without a team token. proxy.php is meant to sit in public_html, so with COUNTERSIGN_TOKEN unset anyone who found the URL could send requests on the operator's Anthropic key. Nothing stopped this except a "strongly recommended" line in the README. It now answers 500 Server not configured: set COUNTERSIGN_TOKEN … until a token is set. ALLOW_NO_TOKEN=1 turns that off for a directory that already has its own access control. This works the same way as proxy.js and engine.js, which won't bind to a public address without a token.

The PHP token compare no longer leaks the token's length. hash_equals() returns early when the lengths differ, even though the README says "neither the token nor its length leaks through timing". Both sides are now hashed with SHA-256 before the compare, which is what the Node servers already do.

A negative max_tokens now falls back to the cap. proxy.js and the engine's /proxy passed max_tokens: -5 straight through, and the API rejects that. proxy.php already treated anything below 1 as missing.

Behaviour change

If an existing PHP deployment has no token set, it will start returning a 500 with a message that names the fix. The README setup steps and troubleshooting table now cover this.

Tests

  • node test/servers.js: 111/111 with PHP 8.4 + curl, up from 106. The new checks are proxy.php refusing to forward without a token, ALLOW_NO_TOKEN=1 overriding that, and all three servers capping a negative max_tokens. Three of the new checks fail on main.
  • node eval/run.js: PASS, unchanged.
  • e2e (Playwright 1.63.0, Chromium): 722/722 checks, 91 tests. index.html is untouched.

…es of the token compare

proxy.php lives in public_html, so with no COUNTERSIGN_TOKEN anyone who found the
URL could spend the operator's API key. It now answers 500 until a token is set,
unless ALLOW_NO_TOKEN=1 says the directory is access-controlled some other way,
matching proxy.js and engine.js, which refuse a public bind without a token.

hash_equals() returns early on a length mismatch, so the token's length leaked
through timing, contrary to what the README says. Both sides are now hashed first.
proxy.js and engine /proxy forwarded max_tokens: -5 as-is, which the API rejects;
proxy.php already treated anything below 1 as missing.
@dboudreau00
dboudreau00 merged commit 503ea4f into main Oct 21, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant