mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-06-06 05:51:19 +08:00
fix(security): move account recovery to local CLI, remove unauthenticated reset endpoints
Unauthenticated POST /api/reset-password and /api/reset-account were a remotely exploitable auth-bypass on public-facing deployments. The confirm phrase was embedded in the frontend and echoed back by the API, so it was friction, not authentication: anyone who knew the account email could reset the password, log in, and obtain a valid JWT. Recovery now runs as local CLI commands that operate directly on the database without starting the HTTP server: nofx reset-password --email you@example.com nofx reset-account These require shell/file access to the host, which a remote attacker does not have, so recovery stays safe even when NOFX is exposed to the public internet. - cli.go: new reset-password / reset-account subcommands (hidden password input on a TTY, --password/stdin for scripting, min 8 chars) - main.go: dispatch subcommands before the server starts (backward compatible with the legacy `nofx <dbpath>` arg) - api: remove public /reset-password and /reset-account routes, their handlers, and the public confirm-phrase constants - web: replace the self-service reset form with CLI instructions; drop the AuthContext resetPassword call and the LoginPage reset-account call (en/zh/id) - telegram: refresh the bot allowlist comment
This commit is contained in:
@@ -165,14 +165,13 @@ func (s *Server) setupRoutes() {
|
||||
// Authentication related routes (no authentication required)
|
||||
s.route(api, "POST", "/register", "Register new user", s.handleRegister)
|
||||
s.route(api, "POST", "/login", "User login, returns JWT token", s.handleLogin)
|
||||
// SECURITY: /reset-password and /reset-account are PUBLIC by necessity —
|
||||
// they ARE the recovery paths when the user can no longer log in. Both
|
||||
// require a literal confirmation phrase in the request body, which
|
||||
// blocks accidental triggers and drive-by scripts. The historical
|
||||
// takeover path (post-reset wallet-key adoption) was closed by
|
||||
// removing adoptOrphanRecords. See handler_user.go for details.
|
||||
s.route(api, "POST", "/reset-password", "Reset password by email (requires confirm phrase)", s.handleResetPassword)
|
||||
s.route(api, "POST", "/reset-account", "[DESTRUCTIVE] Wipe everything (requires confirm phrase)", s.handleResetAccount)
|
||||
// SECURITY: password/account recovery is NOT exposed over HTTP. An
|
||||
// unauthenticated recovery endpoint is a remote auth-bypass on any
|
||||
// public-facing deployment (the confirm phrase is in the frontend and
|
||||
// returned by the API, so it is friction, not authentication). Recovery
|
||||
// is now a local CLI run on the host — `nofx reset-password` /
|
||||
// `nofx reset-account` — which requires shell access the attacker lacks.
|
||||
// See cli.go.
|
||||
|
||||
// Routes requiring authentication
|
||||
protected := api.Group("/", s.authMiddleware())
|
||||
|
||||
Reference in New Issue
Block a user