Skip to content

security(auth): timing-safe compare, cookie session, remove query token#10

Open
BRlin-o wants to merge 3 commits into
lis186:mainfrom
BRlin-o:security/auth-hardening
Open

security(auth): timing-safe compare, cookie session, remove query token#10
BRlin-o wants to merge 3 commits into
lis186:mainfrom
BRlin-o:security/auth-hardening

Conversation

@BRlin-o

@BRlin-o BRlin-o commented Apr 17, 2026

Copy link
Copy Markdown

Summary

Auth hardening for ccxray. Part 2 of 3 PRs from the audit in #7.
Depends on #9 (network surface hardening).

  • Timing-safe token comparecrypto.timingSafeEqual with constant-time length handling (no early return on length mismatch)
  • Remove ?token= query param — leaked in browser history, Referer headers, and proxy logs
  • Cookie-based sessionPOST /login validates token and sets HttpOnly cookie (SameSite=Strict, Secure on non-loopback, 7-day TTL). POST /logout clears it.
  • Login pageGET /login.html serves a minimal token input form. HTML clients get 302 redirect on 401; API clients get JSON.
  • Per-process HMAC secret — sessions invalidate on server restart (acceptable for a local dev tool)

Breaking changes

  • ?token=XXX query auth removed. CLI/curl continues to work with Authorization: Bearer.
  • Browser access with AUTH_TOKEN now goes through /login.html instead of URL param.

Test plan

  • 249 tests pass, 0 failures
  • test/auth.test.js rewritten (11 cases): no-token, wrong Bearer, length-mismatch timing-safe, query param rejection, HTML accept 302, JSON accept 401, /login bypass, valid/tampered cookie
  • test/login.test.js (7 cases): correct/wrong token, cookie session, logout, GET /login 405, Bearer compat, query param rejection
  • Manual: dashboard with AUTH_TOKEN set — redirects to login, token input works, cookie persists

Ref: #7

🤖 Generated with Claude Code

BRlin-o and others added 3 commits April 17, 2026 12:16
Network surface reduction (Phase 0):
- Bind 127.0.0.1 by default; add HOST env for opt-in network access
- Cap request body at 50 MB (CCXRAY_MAX_BODY_MB); return 413 on exceed
- Per-IP SSE connection limit (default 8, CCXRAY_SSE_MAX_PER_IP)
- HTTP timeouts: headersTimeout=60s, requestTimeout=120s, keepAliveTimeout=5s
- Sanitize ANSI/control chars in logged request URLs (log injection)
- Extract readBodyCapped() helper for safe body accumulation

Filesystem hardening (Phase 1):
- Log files 0o600, log directories 0o700; chmod on existing paths
- safeJoin() rejects path traversal (../, /, \, null bytes)
- Hub lockfile and log written with 0o600

Hub localhost-only + validation (Phase 2):
- /_api/hub/* restricted to loopback IPs (127.0.0.1, ::1, ::ffff:127.0.0.1)
- /_api/health remains open (pure liveness, no sensitive info)
- Register/unregister validate pid (int, 0 < n ≤ 2²²) and cwd (string, ≤ 4096)
- Hub body cap 1 KB for control endpoints

BREAKING: Server now binds 127.0.0.1 only. Set HOST=0.0.0.0 for LAN access.

Closes lis186#7 (partial)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bumps basic-ftp to patched version, resolving HIGH severity
CRLF injection in @aws-sdk transitive dependency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace === with crypto.timingSafeEqual for bearer token comparison;
  constant-time even on length mismatch
- Remove ?token= query param auth (leaks in browser history, Referer, logs)
- Add POST /login + POST /logout with HttpOnly cookie session
  (SameSite=Strict, Secure on non-loopback, 7-day TTL)
- HTML Accept requests get 302 to /login.html; API requests get 401 JSON
- Add login.html / login.js — minimal token input form
- Per-process HMAC secret (sessions invalidate on restart — acceptable
  for a local dev tool)

BREAKING: ?token=XXX query auth removed. Use Authorization: Bearer header
(CLI/curl) or the new /login.html flow (browser).

Closes lis186#7 (partial)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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