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:
tinkle-community
2026-06-05 10:49:21 +08:00
parent 2d32a8f6c9
commit 577a0918c3
11 changed files with 335 additions and 389 deletions

2
go.sum
View File

@@ -251,6 +251,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=