Skip to content
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

Tracking through IDP with individualized account and client_metadata endpoints #700

Open
togamid opened this issue Feb 17, 2025 · 9 comments

Comments

@togamid
Copy link

togamid commented Feb 17, 2025

What is preventing an IdP from inserting IDs as path parameters into the endpoints it specifies as a response to the config endpoint and using these IDs to match the accounts and client_metadata requests?
Suppose the response to the config request is as follows:

{
  "accounts_endpoint": "/random_id/accounts",
  "client_metadata_endpoint": "/random_id/metadata",
  "id_assertion_endpoint": "/assertion",
  "login_URL":"/login"
}

This leads to a credentialed request to "/random_id/accounts" and a request with the RP identifier to "/random_id/metadata" without any user interaction in Chromium as long as the IdP returns exactly one account.
An IdP can then use the "random_id" that is consistent between the two requests to combine them and track the user without permission.

I hope I'm wrong and have missed something obvious as FedCM relies heavily on separating these requests and I can't think of an obvious way to fix this.

@wparad
Copy link

wparad commented Feb 17, 2025

Isn't that because the well-known endpoint response and the config.json have to match, and to counter that the random_id in the response must somehow end up being the same or else the browser will reject the config for that exact reason.

My understanding is that is the reason these two requests are made in parallel:

Image

After that, I believe tracked urls no longer matter, because the user will only be tracked in contexts where the user could already be identified. But maybe I'm missing something.

@togamid
Copy link
Author

togamid commented Feb 17, 2025

It is not required to set an accounts_endpoint in the .well-known/web-identity file (see step 21 of this algorithm, there is no action if accounts_endpoint and login_url aren't set) .
The only thing that's required is the config endpoint, which isn't changed in this attack.

But you're right, forcing the account endpoint to be included in the .well-known/web-identity file would make tracking significantly harder (you could probably still give matching responses based on the IP-address or use an individualized config endpoint that gets added to the list of allowed config endpoints in the web-identity file on the fly)

@npm1
Copy link
Collaborator

npm1 commented Feb 18, 2025

Thanks for sharing, I think your attack is correct. We are thinking about mitigations

@npm1 npm1 added the agenda+ Regular CG meeting agenda items label Feb 21, 2025
@hlflanagan hlflanagan removed the agenda+ Regular CG meeting agenda items label Feb 25, 2025
@hlflanagan
Copy link
Contributor

Discussed on 25 February 2025 WG call. See https://github.com/w3c-fedid/meetings/tree/main/2025.

@npm1
Copy link
Collaborator

npm1 commented Feb 25, 2025

A couple of ideas discussed:

  • Store the accounts endpoint string in the user agent the first time we fetch the config file. Then reject the request later if it differs. The big downside of this one is that it will break an IDP if they decide to change their account endpoint location...
  • From @bvandersloot-mozilla: only fetch the client metadata once the user has selected an account and it is deemed to be needed. Downside is latency, also restricts a bit the user agent choices in terms of the UI they could show to the user (not fully compatible with the current Chrome UI, but almost)

@togamid
Copy link
Author

togamid commented Feb 26, 2025

In my opinion the second option isn't possible as the account chooser is not necessarily shown. One such instance is if the RP uses the default value "optional" as the mediation requirement and the IdP only returns one account. If this is the case, the first user interaction happens after the "client metadata" endpoint has already been called.

The downside of the first option is too severe to make it a viable alternative in my opinion. It creates massive headaches for IdPs if they need to change the accounts endpoint for technical reasons (for example because they switch their IdP server software). They would either need to convince every user to reset the stored accounts endpoint (effectively impossible even for small IdPs) or change their domain/subdomain which might require new certificates.

One solution would be to require the inclusion of the accounts URL in the well-known file. If this was the case, the IdP would need to correlate well-known requests with their corresponding config requests to return the same accounts endpoint. There is currently the assumption that that isn't possible (which in my opinion is wrong when considering small IdPs or targeted tracking, but that opens even more, completely separate, tracking options).
The downside of this approach is that all IdPs which have already implemented FedCM have to change their implementation. However, changing a still experimental API is imo still better than using a band-aid that leaves worse problems later.

@npm1
Copy link
Collaborator

npm1 commented Feb 26, 2025

In my opinion the second option isn't possible as the account chooser is not necessarily shown. One such instance is if the RP uses the default value "optional" as the mediation requirement and the IdP only returns one account. If this is the case, the first user interaction happens after the "client metadata" endpoint has already been called.

With "optional", the account chooser is only skipped if this is a user that has used FedCM in this site before, and in that case the IdP already has access to the user in this site. So I think it does work? Also, even when doing auto reauthentication, we do fetch the accounts endpoint.

The downside of the first option is too severe to make it a viable alternative in my opinion. It creates massive headaches for IdPs if they need to change the accounts endpoint for technical reasons (for example because they switch their IdP server software). They would either need to convince every user to reset the stored accounts endpoint (effectively impossible even for small IdPs) or change their domain/subdomain which might require new certificates.

Yea agree that it seems unfeasible.

One solution would be to require the inclusion of the accounts URL in the well-known file. If this was the case, the IdP would need to correlate well-known requests with their corresponding config requests to return the same accounts endpoint. There is currently the assumption that that isn't possible (which in my opinion is wrong when considering small IdPs or targeted tracking, but that opens even more, completely separate, tracking options). The downside of this approach is that all IdPs which have already implemented FedCM have to change their implementation. However, changing a still experimental API is imo still better than using a band-aid that leaves worse problems later.

Yea enforcing accounts_endpoint in well-known is indeed another option. As you mention, it does not fully solve the problem, but it may to some extent. The backwards incompatibility issue is my concern here - I get that this API is still not shipped in multiple browser vendors, but we have shipped in Chrome for a while, so I would strongly prefer a backwards compatible solution. That said, I'd consider it if it was the only viable solution.

@togamid
Copy link
Author

togamid commented Feb 26, 2025

With "optional", the account chooser is only skipped if this is a user that has used FedCM in this site before, and in that case the IdP already has access to the user in this site.

I might be reading the spec incorrectly, but a first request with "optional" with exactly one account should trigger step 26 of the Create an Identity Credential Algorithm, which does not include an account chooser. Thus, the first user interaction is after the metadata endpoint has been requested as part of the Request permission to sign up Algorithm triggered in step 26.3 .

And from my testing in Chromium I can't remember seeing an account chooser when only providing one account (though that was with RP and IdP both being on 127.0.0.1, which might have changed things)

Even if there was a required user interaction before the sign-up dialogue that isn't there the second time, the dates and frequency a user visits a specific website is still valuable information that might be exposed to the IdP without permission in that case. (Though it looks like the privacy policy etc is not displayed on a second login and the client metadata endpoint not queried, which currently prevents the attack on a second login but could cause problems if the privacy policy changes).

In general, the case that the user only has one account is probably more likely than the user having multiple accounts, so forcing an account chooser which requires an (often unnecessary) extra click is bad UX. With a solution like this there's also a chance that a future change requests wants to improve the UX and inadvertently reopens the vulnerability.

As a side note, I've just discovered that the attack presented here is a variation on this issue from 2022: #230

@npm1
Copy link
Collaborator

npm1 commented Feb 26, 2025

I might be reading the spec incorrectly, but a first request with "optional" with exactly one account should trigger step 26 of the Create an Identity Credential Algorithm, which does not include an account chooser. Thus, the first user interaction is after the metadata endpoint has been requested as part of the Request permission to sign up Algorithm triggered in step 26.3 .

Yea well currently all UI is shown after the client metadata fetch, so that would need to change. That's the point of the idea though, in this case some account confirmation dialog would be shown before the client metadata fetch.

And from my testing in Chromium I can't remember seeing an account chooser when only providing one account (though that was with RP and IdP both being on 127.0.0.1, which might have changed things)

Yea in Chrome we show the PP/TOS right away on the first screen if there is a single user. That is what I was alluding to when I mentioned this constraints the UI somewhat and in particular not fully compatible with the existing Chrome UI.

In general, the case that the user only has one account is probably more likely than the user having multiple accounts, so forcing an account chooser which requires an (often unnecessary) extra click is bad UX. With a solution like this there's also a chance that a future change requests wants to improve the UX and inadvertently reopens the vulnerability.

I do agree adding a click seems worse UX. It would be best if we can solve this in a way to avoid it, but for now this seems the most feasible option.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants