An account can be got back into without email, or SMTP can never be switched off #103
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Blocks
#144 The account's primary number is a way back in
Postulo/postulo
Reference
Postulo/postulo#103
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why this exists
#100 settled that SMTP may be switched off only when it is not the last way anybody could
get back into their account -- the same rule Postulo already applies to the last
administrator, one level deeper.
Checking what that interlock would actually find:
server_urls.pygives an administrator four powers over an account -- make administrator,deactivate, rename, delete -- and none of them is help this person get back in. So on an
instance without SSO, email is the only route that exists, and the interlock will refuse
every attempt to switch SMTP off. Correctly, and permanently, until something else exists.
What would count as a recovery route
Not "a way to reach somebody". A recovery route has to deliver a single-use, expiring,
account-scoped token to a channel already proven to reach the right person, and it has to
be at least as hard to intercept as the email it replaces.
That last clause matters. A Telegram chat configured six months ago and never tested since is
not a recovery route; it is a guess. The
Connectionmodel already recordslast_ok_atandlast_errorand has a Test button, so "proven" is available rather than hypothetical -- andshould be required, with an age limit.
Three candidates, in increasing order of how much they need thinking about
1. An administrator issues a reset link. Smallest, and probably first. An administrator
generates a single-use link and hands it over by whatever means they already trust -- in
person, on the phone. No new channel, no new dependency, and it is the answer for a family
or small-team instance where the administrator is in the same room. Needs care: the link is a
full account takeover in a URL, so it wants a short life, one use, an audit record, and it
must never be shown to anybody but the administrator who asked for it.
2. A notifier a person has already proven. Apprise, ntfy, Telegram, Signal. The plugin
machinery is already there and the connection is already owned, tested and dated. The
question is not whether it can deliver but whether it should carry credentials: a notifier is
configured for convenience and may point at a shared channel, a family group, a desktop
alert on an unlocked machine. Somebody choosing a notifier for reminders did not choose it
for account recovery. So this needs an explicit opt-in per connection -- "use this to get
back in" -- rather than treating every notifier as a recovery route by default.
3. SMS. Named in the request, and the weakest of the three despite being the most
familiar: it needs an outbound gateway (a cost and a dependency Postulo does not have), and
SIM-swap attacks make it the channel security guidance has been moving away from for a
decade. If it is built, it should be an ordinary notifier plugin like any other rather than
something Postulo speaks natively -- which is candidate 2 again, with a different transport.
What has to be true whichever is built
instance.
ACCOUNT_EMAIL_VERIFICATION = "mandatory"means a newaccount still needs a verification email, so an instance with SMTP off can recover its
existing people and cannot admit new ones. #100 has to say which of those it covers.
Classification
Enhancement, security. Prerequisite for the SMTP half of #100 ever answering yes; nothing
else waits on it.
Two of the three missing routes in the table above now have issues of their own: #143 for a channel the instance can reach somebody on, and #144 for the telephone number at the end of it. #142 is the one that has to come first — the numbers stored since #90 are unverified by design, and a recovery route on an unverified identifier is takeover rather than recovery.
Worth noting for this issue: an administrator-issued recovery link, which the table lists as the other gap, needs no third party at all. On a self-hosted instance with one administrator it answers the same question as SMS without sending a telephone number to an aggregator, and it does not wait on any of the three.
Candidate 1, built. Server settings → People → ⋯ → Recovery link: an
administrator makes a single-use link and hands it over themselves. No third party, no
gateway, no cost — the reason it comes before SMS rather than after it.
The four cautions in the issue are each a property of the thing:
week.
link-preview bot in the chat an administrator sent it through would otherwise burn
somebody's only way back in by fetching it.
it and never again. Nothing stores the token; the row keeps a SHA-256 fingerprint, so the
record is a record rather than a second copy of the secret. It is never mailed, which would
be absurd for the route that exists to replace mail.
it was used, revoked or left to expire. There is still no general audit trail in Server
settings, and a full account takeover leaving no trace at all was not a thing to wait for
one with.
Two decisions beyond what was asked, both towards a smaller blast radius. It sets a
password and does not sign anybody in, so a link that goes astray does not bypass a second
factor. And the token is not in the address of the form it leads to — the ticket moves to
the session on the first hop — so it never travels in a
Refererheader or a synced history.Who the route reaches, from "the route is per person". Issuing needs signing in, and the
person who forgot their password cannot: so it reaches everybody but the issuer. Two
administrators cover each other; one covers everybody else; a lone administrator without a
passkey is the single account it does not reach, and the page says so. Counted rather than
assumed, so it changes the moment somebody is made an administrator.
On "it does not solve sign-up" — said where it matters. The Email page now warns, when
the lock is open and registration is on, that mail may be switched off but nobody could create
a new account, because a new account has to verify an address before it exists. That is the
half #100 was told to be explicit about, and it is now on the page somebody acts from rather
than discovered when the first sign-up fails.
POSTULO_RECOVERY_RATE(10/h) bounds issuing: each link is a whole account in a URL, so acompromised administrator session cannot mint fifty.
Candidates 2 (a notifier the person has proven, with per-connection opt-in) and 3 (SMS) are
untouched and still worth having; #143 built the channel the third would need and recorded
why it is not the first answer.
30 tests in
tests/test_recovery_links.py, all three new pages walked by the browser suiteunder axe-core, a wiki section under Accounts and invitations, and thirty strings in all 39
European catalogues.
Shipped in
f8bc6e7on0.3.0, withmainkept level.