-
Notifications
You must be signed in to change notification settings - Fork 221
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Docs should make clear that setting oidc_providers.allow_existing_users: true
has security implications
#17837
Comments
Thanks for the very detailed report! This is a well-known behaviour and intended to work like this, as the main usage we've seen of this feature is for deployments moving off a password-based auth to an SSO-based one. In those cases, it would be impractical to ask the user to re-enter their original credentials, as the intent is to make it as smooth as possible for end users. This is what the docs says about the option: synapse/docs/usage/configuration/config_documentation.md Lines 3465 to 3467 in 2ce7a1e
Two things to note:
Which makes me feel like a sensible homeserver administrator would not enable this with a public IDP, but maybe with one they control. I'm happy with expanding this particular point in the doc to warn about the potential misuse of this option, but would rather avoid changing any of the existing behaviour. Last but not least, Synapse's auth is being reworked through Matrix Authentication Service, which is quite stricter on this, and currently doesn't have this 'allow existing users' feature. |
Yes - automatic account linking makes migration to SSO marginally smoother, but it's not secure unless based on claims which are unique and stable at the IdP, and not under user control. This is a similar bug in Mastodon, where it incorrectly asserted that As I understand it, there's no alternative to auto-linkage-on-claims for migrating existing accounts to SSO, aside from perhaps manually editing your Matrix server's user database. This may be an option for some, but isn't a great admin experience. A secure automated migration strategy for an existing Matrix user could be done a couple of ways:
Or:
Both of those migration flows would ensure that the user signing in with OIDC holds an existing Matrix credential for the account before setting up any linkage, while still being fairly smooth. This would also automatically handle someone who uses a different username/localpart for Matrix and the IdP, or has changed their name during the migration process, as they would provide their Matrix localpart directly. For an organisation running their own Matrix server, they might only want to offer OIDC first, and only fall back to non-OIDC if a user doesn't already have an account. If the organisation can ensure the claim(s) used for For a public Matrix server, they may wish to offer authentication with one or more public IdPs in addition to local login, but have some fallbacks in place to warn someone if they might already have an account. But in a public server, auto-linkage requires users fully trust all available IdPs – not just the IdP(s) they use. The complication is doing all of this is providing a protocol for a Matrix server to signal the OIDC migration offer (or demand) to a Matrix client. 😄
Yes, However, the Shibboleth sample configuration sets this to
There are several examples of public IdPs in the docs, and all of those set a While it's fairly obvious that a |
The
oidc_providers.allow_existing_users
documentation is pretty sparse:The way the sample configurations do this makes assumptions contrary to OIDC claim stability guidelines. If a Synapse instance was configured in this way, it could allow account take-overs1 by an attacker with an unknown-to-Synapse IdP account.
The documentation should describe the security implications of enabling the option.
While there are some weaknesses in Synapse that make this possible at all, the ability to exploit it is mostly configuration dependant – so I feel this is a "security-related docs bug" rather than "docs-related security bug".
Background
From OpenID Connect Basic Client Implementer's Guide 1.0, Section 2.5.3, Claim Stability and Uniqueness, emphasis added:
By default, Synapse uses the
sub
claim to uniquely identify a user, which is in-line with OIDC recommendations.Detail
Setting
oidc_providers.allow_existing_users: true
triggers an extra code path ingrandfather_existing_users()
on login if lookup by external user ID fails.This maps an OIDC user to a Matrix user based on
localpart_template
:synapse/synapse/handlers/oidc.py
Lines 1247 to 1255 in 2ce7a1e
If there is an existing Matrix user which maps, the new
sub
is added to the database:synapse/synapse/handlers/sso.py
Lines 474 to 479 in 2ce7a1e
As there doesn't seem to be any further authentication (such as requesting a user login with a password, or with another configured external identity provider), my working assumption (from static analysis) is that enabling
allow_existing_users
allows an unknown-to-Synapse OIDC user to permanently link their OIDC identity to an existing Matrix account that matches bylocalpart_template
.I couldn't find anywhere that branch checks to see if a matched account already has an
user_external_id
associated with it (only that an external ID can't be re-used), so it looks like this would allow two OIDC users to "claim" the same Matrix account.One caveat is that if an instance sets
enable_registration: true
(the default), an attacker would need a new IdP account for every attempt they made to take over an account.The
localpart_template
The impact of the issue has depends on the
localpart_template
.There is no default
localpart_template
, but the example OIDC configs doc have some suggestions, and many of them look insecure:Entra ID (Azure AD): Using everything before an
@
character inpreferred_username
.Entra ID sets the
preferred_username
to the account email address, so this would treatuser@example.com
anduser@example.net
as the same user.Entra ID also has a tenancy ID attribute, but this doesn't seem to be used.
Django OAuth Toolkit: Using everything before an
@
character inemail
.Similar issue to Entra ID.
Shibboleth: Using everything before an
@
character inuser.sub
, and also setsallow_existing_users: true
.Shibboleth has custom mapping rules for
sub
, but this example seems to suggestsub
is anemail
-like field.If it is an email field, then this would have a similar issue to Entra ID, with an insecure-by-default config.
Auth0, Authentik, Keycloak, LemonLDAP: Using
preferred_username
as-is – this is an unstable claim.Twitch: Using
preferred_username
as-is.This is the display name, and can be changed by users, or recycled by Twitch.
GitHub, GitLab, Gitea, Mastodon, Twitter: Using
user.login
/user.nickname
/user.username
as-is.Those accounts may be renamed.
Google: Using
user.given_name
as-is.This may be freely changed by the user.
If an attacker with an unknown-to-Synapse IdP account can discover a Synapse server's
localpart_template
, and find that it is under user control, they could exploit a server withoidc_providers.allow_existing_users: true
:enable_registration: true
(the default), an attacker would need a new IdP account for each takeover attempt, though this is a low bar for a public identity provider.localpart_template
to match the target user.It's not clear to me (from static analysis) whether a user can discover what external user IDs are associated with their account; though it would appear as another unverified session (as they wouldn't have the E2EE keys).
What could change in the docs?
oidc_config.allow_existing_users
should describe how it maps existing users (withlocalpart_template
), and that enabling the option has significant security implications.It should also note that enabling the option adds extra constraints to any claim used by
localpart_template
:matrix_username
) in the IdP to map users, rather than using other, potentially-unstable identifiers (likeemail
orusername
)What could change in Synapse?
When "grandfathering" existing Matrix user accounts, Synapse should also authenticate the user by some other method, even if those methods are not normally available (eg: password backend). This would prevent account take-overs during the migration process.
There should be a way to prevent more than one external user ID being assigned to the same Matrix user. This would prevent account take-overs for accounts which are already migrated to OIDC.
Footnotes
If a user has enabled end-to-end encryption and uses it in all channels, I suspect it would prevent an attacker from accessing prior messages or impersonating the user on those channels. ↩
The text was updated successfully, but these errors were encountered: