Skip to content

security(enterprise-api): client IP for rate limiting comes from client-controllable headers #118

Closed

Description

@sre-helmcode

Summary

enterprise-api derives the client IP from headers the client can influence, so any per-IP control built on it can be spoofed or mis-keyed.

  • cmd/server/routes.go:106 uses chimw.RealIP, which trusts True-Client-IP, X-Real-IP and X-Forwarded-For from ANY peer and overwrites RemoteAddr.
  • internal/handlers/auth.go remoteIP() (used for rate limiting) takes the LEFTMOST X-Forwarded-For value.

Found as an out-of-scope note in the security review of helmcode/nan-devops#623 (nan#114). Not verified end to end yet.

Why it matters

Traffic path: Cloudflare -> cloudflared -> Traefik web -> enterprise-api.

  • Cloudflare APPENDS the connecting IP to a client-supplied X-Forwarded-For, so the leftmost value is attacker-chosen, and it passes a client-supplied X-Real-IP through.
  • Depending on Traefik forwardedHeaders trust for the cloudflared pods, the header that reaches enterprise-api is either the attacker's value (per-IP rate limits on magic-link/login can be bypassed by rotating it) or the cloudflared pod IP (every user shares one bucket, so one abuser can rate-limit everyone).
  • Either way the per-IP rate limit on the auth endpoints does not key on the real client.

Proposed fix

  1. Verify what actually reaches the pod: log RemoteAddr, X-Forwarded-For, X-Real-IP, True-Client-IP and Cf-Connecting-Ip for one request sent with forged values through api.helmcode.com / console.helmcode.com.
  2. Replace chimw.RealIP + leftmost-XFF with one helper that trusts Cf-Connecting-Ip only (set by Cloudflare, overwrites any client value on the web path; nan#114 / nan-devops#623 strips it on websecure), falls back to RemoteAddr, and never reads XFF/X-Real-IP/True-Client-IP.
  3. Tests: forged XFF / X-Real-IP / True-Client-IP must not change the rate-limit key; Cf-Connecting-Ip must.
  4. Check cloud-api for the same pattern.

Tenant: Helmcode enterprise (possibly NaN community too if cloud-api does the same).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions