fix(backend): disable one-registration-per-IP by default

Shared NAT egress makes client IP unusable for uniqueness checks. Gate the
behavior behind REGISTRATION_ENFORCE_IP (default false) for public/invite signup.
This commit is contained in:
ahp
2026-10-07 23:13:22 +03:30
parent 92b1681241
commit 3cce2158e9
15 changed files with 103 additions and 53 deletions
+1
View File
@@ -63,6 +63,7 @@ One of two patterns:
|----------|-----------------|--------|
| `DEV_MODE` | false | Open metrics without token, limited fail-open behavior |
| `SEED_CURRICULUM` | false (empty in DevMode → true) | Seed lessons/events |
| `REGISTRATION_ENFORCE_IP` | false | One registration per client IP; keep off behind NAT |
| `CAPTCHA_EXPOSE_ANSWER` | DevMode only | For tests; never in production |
| `DONATION_ENABLED` | true | Support card |
| `DONATION_NOTE` | Copy text | |
+1 -1
View File
@@ -28,7 +28,7 @@
- Whitelist and blacklist are **mutually exclusive**; import resolves conflicts.
- A phone already tied to an account cannot re-enter either list as a fresh entry.
- One account per phone and per IP on registration paths.
- One account per phone. Optional one-account-per-IP (`REGISTRATION_ENFORCE_IP`, default off — unsuitable behind NAT).
- Blacklist blocks all registration paths (public + invite) and keeps a failed-attempt history tab.
## Certificates
+5 -1
View File
@@ -25,6 +25,10 @@ Public password recovery (`/api/auth/recovery/*` and `/recovery`) is disabled fo
- Only peers in `TRUSTED_PROXY_CIDRS` are trusted for `X-Forwarded-*` / `X-Real-IP`.
- CORS: `FRONTEND_ORIGINS` + `PUBLIC_URL` with `AllowCredentials: true`.
## Registration IP binding
`REGISTRATION_ENFORCE_IP` (default **false**) optionally limits one successful public/invite registration per client IP. Leave it off when the app sits behind NAT or shared egress and cannot see real client addresses. Enable only after the edge forwards trustworthy `X-Forwarded-For` / `X-Real-IP` via `TRUSTED_PROXY_CIDRS`.
## Rate limiting
| Scope | Approximate behavior |
@@ -34,7 +38,7 @@ Public password recovery (`/api/auth/recovery/*` and `/recovery`) is disabled fo
| External chat export | ~10/min/IP |
| Per-user uploads | Separately limited |
`/api/health` is excluded from the global ceiling.
`/api/health` is excluded from the global ceiling. Note: per-IP rate limits still use the peer/forwarded address — configure trusted proxies when you need per-client fairness behind NAT.
## Uploads