Trust the relay you typed, pin the relays that are ours
Correcting a rule I got wrong earlier today. I had the client accept a named
account service only if it was on a pinned list containing exactly
accounts.flamenet.io — which is safe and breaks the case the server already
supports: a self-hoster running Messenger and Files together, with their own
account service holding the one account across both. The file server has taken
ENCLAVE_ACCOUNTS_URL for exactly that since it shipped, and main's relay makes
FN_ACCOUNTS_URL configurable for the same reason. A client refusing the answer
would make "run both" mean "run them with two separate logins".
The rule is asymmetric, and the asymmetry is the point:
- For flamenet.io and its aliases the account service is PINNED in the client
and whatever those relays advertise is ignored. Compromising one of ours
must not let it redirect its users' passphrase-derived secrets.
- Every other relay is believed. The user typed that hostname themselves; a
server they chose naming its own account service is a choice they already
made, and it cannot redirect anybody else's users.
Two tests, one for each half: a pinned relay advertising an attacker's service
still authenticates against ours, and a self-hosted relay may name its own.
This commit is contained in:
@@ -54,14 +54,13 @@ data class SiteConfig(val baseUrl: String) {
|
||||
/**
|
||||
* Where this relay's accounts live.
|
||||
*
|
||||
* ⚠️ **Asked of the relay, then checked against a pinned list of account
|
||||
* SERVICES.** It used to be a hardcoded list of relay hostnames, which was
|
||||
* safe and wrong: a self-hosted relay could never be on it, so the
|
||||
* "self-hosted, shared identity" shape was unreachable. Now the relay says
|
||||
* which service it uses and the client decides whether that service is one
|
||||
* it will send a derived secret to — see
|
||||
* [io.flamenet.client.RelayDiscovery.TRUSTED_ACCOUNT_SERVICES]. A rogue
|
||||
* operator can only name a service the client already trusts.
|
||||
* ⚠️ **Asked of the relay, except for the relays that are ours.** For
|
||||
* flamenet.io and its aliases the account service is pinned in the client
|
||||
* and what they advertise is ignored, so compromising one cannot redirect
|
||||
* its users' derived secrets. Every other relay is believed: the user typed
|
||||
* that hostname, and a self-hoster running Messenger and Files together
|
||||
* needs their own account service to be usable across both. See
|
||||
* [io.flamenet.client.RelayDiscovery.PINNED_RELAYS].
|
||||
*
|
||||
* Defaults to LOCAL until the descriptor has been fetched, which is the
|
||||
* safe direction: the passphrase goes only to the server the user typed.
|
||||
@@ -76,7 +75,7 @@ data class SiteConfig(val baseUrl: String) {
|
||||
}
|
||||
|
||||
val accountServiceUrl: String
|
||||
get() = discovered?.accountsUrl ?: SHARED_ACCOUNT_SERVICE
|
||||
get() = discovered?.effectiveAccountsUrl ?: SHARED_ACCOUNT_SERVICE
|
||||
|
||||
val usesSharedAccounts: Boolean get() = accountProvider == AccountProvider.SHARED
|
||||
|
||||
|
||||
Reference in New Issue
Block a user