Skip to content

fix(security): use constant-time bearer-token comparison in client API#114

Merged
hyp3rd merged 5 commits intomainfrom
feat/dist-mem-cache
May 6, 2026
Merged

fix(security): use constant-time bearer-token comparison in client API#114
hyp3rd merged 5 commits intomainfrom
feat/dist-mem-cache

Conversation

@hyp3rd
Copy link
Copy Markdown
Owner

@hyp3rd hyp3rd commented May 6, 2026

Replace the plaintext got != want string check in bearerAuth
(cmd/hypercache-server/main.go) with crypto/subtle.ConstantTimeCompare.
A naive string compare returns on the first differing byte, leaking
per-byte equality of HYPERCACHE_AUTH_TOKEN to an attacker who can
measure response latency. The fix mirrors the constant-time check
already present in the dist transport (pkg/backend/dist_http_server.go).

No public API change; the env-var contract and the zero-config
"empty token → open mode" backcompat behaviour are preserved.

Add cmd/hypercache-server/auth_test.go to pin the auth contract:

  • Missing, wrong, malformed, lowercase-scheme, and wrong-length
    Authorization headers all return 401 on protected routes.
  • Public meta routes (/healthz, /v1/openapi.yaml) remain reachable
    without credentials (K8s liveness probes, self-describing spec).
  • All cache and batch endpoints are asserted to require auth, catching
    future wiring regressions when new routes are added without the
    bearerAuth wrapper

hyp3rd added 5 commits May 6, 2026 14:57
…etection

Introduce a complete, self-describing OpenAPI 3.1 contract for the
hypercache-server client REST API, with tooling to ensure the spec
and binary never silently drift apart.

- Add `cmd/hypercache-server/openapi.yaml`: covers all 11 routes
  (single-key PUT/GET/HEAD/DELETE, owners lookup, three batch
  operations, /healthz, /v1/openapi.yaml) with reusable component
  schemas, bearerAuth security scheme, and operationId on every
  operation for codegen friendliness.

- Embed spec via `//go:embed` in `openapi.go` and serve it at
  `GET /v1/openapi.yaml` — every running node is self-describing,
  and the deployed contract is always in lockstep with the binary.

- Extract `registerClientRoutes` from `runClientAPI` so the drift
  test (and handler tests) can drive the exact production route
  wiring without spinning up a real listener.

- Add `openapi_test.go` drift detector: asserts every fiber route
  in `registerClientRoutes` has a matching path in `openapi.yaml`
  (and vice-versa), preventing silent spec/code divergence.

- Add `.github/workflows/openapi.yml` CI workflow with two jobs:
  `redocly lint` validates the spec against the OpenAPI 3.1
  meta-spec; the Go drift test runs on every change to `main.go`
  or the spec.

- Add `redocly.yaml` config (extends `recommended`, disables two
  intentional no-ops: localhost server URL and missing 4xx on
  auth-free meta endpoints).

- Add `docs/api.md` with inline Swagger UI powered by
  `mkdocs-swagger-ui-tag`; `_mkdocs/hooks.py` injects the binary's
  embedded spec as a virtual docs asset via an `on_files` hook,
  keeping a single source of truth across binary, docs, and codegen.

- Promote `gopkg.in/yaml.v3` from indirect to direct dependency
  (used by the drift test).
Replace the plaintext `got != want` string check in `bearerAuth`
(cmd/hypercache-server/main.go) with `crypto/subtle.ConstantTimeCompare`.
A naive string compare returns on the first differing byte, leaking
per-byte equality of HYPERCACHE_AUTH_TOKEN to an attacker who can
measure response latency. The fix mirrors the constant-time check
already present in the dist transport (pkg/backend/dist_http_server.go).

No public API change; the env-var contract and the zero-config
"empty token → open mode" backcompat behaviour are preserved.

Add cmd/hypercache-server/auth_test.go to pin the auth contract:
- Missing, wrong, malformed, lowercase-scheme, and wrong-length
  Authorization headers all return 401 on protected routes.
- Public meta routes (/healthz, /v1/openapi.yaml) remain reachable
  without credentials (K8s liveness probes, self-describing spec).
- All cache and batch endpoints are asserted to require auth, catching
  future wiring regressions when new routes are added without the
  bearerAuth wrapper.
Introduce pkg/httpauth — the v2 successor to the single-token
bearerAuth helper — with the following capabilities:

- Multi-token bearer auth: multiple TokenIdentity entries each
  carrying explicit scopes (read/write/admin), resolved in
  constant time across all configured tokens to prevent
  token-cardinality timing leaks.
- Per-route scope enforcement: GET/HEAD/owners-lookup/batch-get
  require ScopeRead; PUT/DELETE/batch-put/batch-delete require
  ScopeWrite.
- mTLS support: HYPERCACHE_API_TLS_CERT + HYPERCACHE_API_TLS_KEY
  enable standard TLS; adding HYPERCACHE_API_TLS_CLIENT_CA upgrades
  to RequireAndVerifyClientCert with peer CN mapped to CertIdentity.
- YAML policy config via HYPERCACHE_AUTH_CONFIG; legacy
  HYPERCACHE_AUTH_TOKEN still works byte-identical as a single
  all-scopes identity shortcut. Both env vars are orthogonal:
  CONFIG governs the client API, AUTH_TOKEN drives dist peer auth.
- Fail-closed startup: loadConfig() now returns an error and exits
  non-zero on missing/malformed HYPERCACHE_AUTH_CONFIG. Zero-config
  dev posture preserved via AllowAnonymous with a startup warning.

Add pkg/httpauth/{policy,loader,policy_test,loader_test}.go and
cmd/hypercache-server/{mtls_test,mtls_e2e_test}.go. Update
auth_test.go and openapi_test.go to use httpauth.Policy. Document
auth surfaces in CHANGELOG and docs/distributed.md.
Introduce pkg/httpauth — the v2 successor to the single-token
bearerAuth helper — with the following capabilities:

- Multi-token bearer auth: multiple TokenIdentity entries each
  carrying explicit scopes (read/write/admin), resolved in
  constant time across all configured tokens to prevent
  token-cardinality timing leaks.
- Per-route scope enforcement: GET/HEAD/owners-lookup/batch-get
  require ScopeRead; PUT/DELETE/batch-put/batch-delete require
  ScopeWrite.
- mTLS support: HYPERCACHE_API_TLS_CERT + HYPERCACHE_API_TLS_KEY
  enable standard TLS; adding HYPERCACHE_API_TLS_CLIENT_CA upgrades
  to RequireAndVerifyClientCert with peer CN mapped to CertIdentity.
- YAML policy config via HYPERCACHE_AUTH_CONFIG; legacy
  HYPERCACHE_AUTH_TOKEN still works byte-identical as a single
  all-scopes identity shortcut. Both env vars are orthogonal:
  CONFIG governs the client API, AUTH_TOKEN drives dist peer auth.
- Fail-closed startup: loadConfig() now returns an error and exits
  non-zero on missing/malformed HYPERCACHE_AUTH_CONFIG. Zero-config
  dev posture preserved via AllowAnonymous with a startup warning.

Add pkg/httpauth/{policy,loader,policy_test,loader_test}.go and
cmd/hypercache-server/{mtls_test,mtls_e2e_test}.go. Update
auth_test.go and openapi_test.go to use httpauth.Policy. Document
auth surfaces in CHANGELOG and docs/distributed.md.
Introduce pkg/httpauth — the v2 successor to the single-token
bearerAuth helper — with the following capabilities:

- Multi-token bearer auth: multiple TokenIdentity entries each
  carrying explicit scopes (read/write/admin), resolved in
  constant time across all configured tokens to prevent
  token-cardinality timing leaks.
- Per-route scope enforcement: GET/HEAD/owners-lookup/batch-get
  require ScopeRead; PUT/DELETE/batch-put/batch-delete require
  ScopeWrite.
- mTLS support: HYPERCACHE_API_TLS_CERT + HYPERCACHE_API_TLS_KEY
  enable standard TLS; adding HYPERCACHE_API_TLS_CLIENT_CA upgrades
  to RequireAndVerifyClientCert with peer CN mapped to CertIdentity.
- YAML policy config via HYPERCACHE_AUTH_CONFIG; legacy
  HYPERCACHE_AUTH_TOKEN still works byte-identical as a single
  all-scopes identity shortcut. Both env vars are orthogonal:
  CONFIG governs the client API, AUTH_TOKEN drives dist peer auth.
- Fail-closed startup: loadConfig() now returns an error and exits
  non-zero on missing/malformed HYPERCACHE_AUTH_CONFIG. Zero-config
  dev posture preserved via AllowAnonymous with a startup warning.

Add pkg/httpauth/{policy,loader,policy_test,loader_test}.go and
cmd/hypercache-server/{mtls_test,mtls_e2e_test}.go. Update
auth_test.go and openapi_test.go to use httpauth.Policy. Document
auth surfaces in CHANGELOG and docs/distributed.md.
@hyp3rd hyp3rd merged commit 28567c9 into main May 6, 2026
16 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