Skip to content

[CFX-7754] Reject browser-forged API keys during dr auth login - #820

Open
chasdr wants to merge 2 commits into
mainfrom
chas/CFX-7754
Open

[CFX-7754] Reject browser-forged API keys during dr auth login#820
chasdr wants to merge 2 commits into
mainfrom
chas/CFX-7754

Conversation

@chasdr

@chasdr chasdr commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Summary

During dr auth login the CLI runs a local listener on localhost:51164 for 5 minutes, waiting for the browser to hand back your API key. It accepted any request on that port, so a web page you had open could plant an attacker's key with <img src="http://localhost:51164/?key=ATTACKER_KEY"> and the CLI would store it as your credential, so every later dr command ran against the attacker's account. The callback now refuses the forgeable browser requests.

Notes for review

The gate is Sec-Fetch-Dest present and not document. Absent must stay accepted: the CLI-to-CLI port handover uses a Go http client that sends no fetch metadata, and rejecting absent would deadlock two concurrent logins. It does not gate on Sec-Fetch-Site, since the real callback is legitimately cross-site.

Partial by design. It does not stop a page that forces a full-page navigation (a real document request, visible in the tab) or browsers older than Safari 16.4. The complete fix is a callback nonce, tracked separately since it needs a web app change.

Blocking manual gate, a real login must still work on:

  • Chrome (macOS): pass
  • Firefox: pending
  • Safari: pending
  • Edge: pending

Output

$ curl -s -o /dev/null -w "%{http_code}\n" -H "Sec-Fetch-Dest: image" "http://localhost:51164/?key=PLANTED-BY-ATTACKER"
403
$ curl -s -o /dev/null -w "%{http_code}\n" -H "Sec-Fetch-Dest: document" "http://localhost:51164/?key=REAL-KEY"
200

Technical Changes

  • browserflow.go: refuse when Sec-Fetch-Dest is present and not document; success page only for a keyed request, 204 for the keyless handover sentinel.
  • browserflow_test.go: image/empty refused, document accepted, headerless accepted, keyless image no longer interrupts, keyless headerless still hands over.
  • auth docs: corrected the security note, added the editing rule for the gate.

Breakdown

  • code: +17 / -5
  • tests: +103 / -3
  • docs: +15 / -5

Note

High Risk
Touches the login callback that accepts and stores API keys. The gate is partial by design (no state/nonce), so mistakes here can still accept attacker credentials or break concurrent logins.

Overview
Hardens dr auth login so a page cannot plant an API key via a forgeable request (<img> / fetch) to the localhost callback while login is in flight.

handleCallback now returns 403 when Sec-Fetch-Dest is present and not document. An absent header is still accepted so CLI-to-CLI port handover (Go http.Client, no fetch metadata) does not deadlock. Keyless handover gets 204 instead of the success HTML; a keyless browser probe (e.g. favicon) is refused and no longer aborts login.

This is a partial mitigation: full-page navigation and clients that can set headers are still in scope. Docs and tests cover the gate, genuine document callbacks, and the handover sentinel.

Reviewed by Cursor Bugbot for commit 385eb4a. Configure here.

chasdr added 2 commits August 21, 2026 18:05
Why:
The `dr auth login` callback listener on localhost:51164 accepted any
request for its 5-minute window. A cross-origin `<img src=".../?key=...">`
on any open page could plant an attacker's API key, which the CLI then
stored as the user's credential. A stray keyless request (favicon probe)
was also misread as the CLI-to-CLI port-handover sentinel and aborted the
login.

Changes:
- handleCallback refuses when Sec-Fetch-Dest is present and not "document".
  The real callback is a top-level navigation; a page cannot forge
  Sec-Fetch-* headers, so an img/fetch is turned away. Absent stays
  accepted, which keeps the port handover (a Go client sends no such
  header) working.
- Success page is written only when a key is present; the keyless sentinel
  gets a 204, which the handover's Go client never reads.
- Tests for image/empty refused, document accepted, headerless accepted,
  keyless image no longer interrupts, keyless headerless still does.

Partial by design: does not stop a top-level navigation a hostile page
forces (a real document request, but visible), or browsers without fetch
metadata. The complete fix is a callback nonce and needs a paired web app
change, tracked separately.
@chasdr
chasdr requested a review from a team as a code owner August 21, 2026 22:12
@datarobot-pr-review-router

Copy link
Copy Markdown

🎫 Jira: CFX-7754 — Auth callback listener accepts any browser request, so a page can plant an API key during login

@github-actions github-actions Bot added the go Pull requests that update go code label Aug 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

go Pull requests that update go code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant