Open lawnsea opened 7 years ago
As a workaround, the site can "opt out" of the Credential Management API by clobbering navigator.credentials
:
if (navigator.credentials) {
Object.defineProperty(navigator, 'credentials', {
configurable: false,
enumberable: true,
value: {
get: () => Promise.reject('Credentials API disabled')
},
writable: false
});
}
This depends on how the user agent treats host objects, however, so it may not be a reliable mitigation across browsers.
When I follow your steps to produce, I get the following output:
This page will prompt the user to log in and then output their login credentials below.
Exploit failed.
The expected (and hopefully implemented) behavior in Chrome is the following:
navigator.credentials.get(..., mediation: silent)
--> it won't get anything.
b.example.com can call navigator.credentials.get(..., mediation: optional)
--> a UI pops up to ask for confirmation to give b.example.com the credentials.navigator.credentials.store()
. For subsequent visits, navigator.credentials.get(..., mediation: silent)
will return the credential to b.example.com also silently because the credential has been stored for that specific security origin.I suggest to address this issue as follows
When I follow your steps to produce, I get the following output:
Exploit failed.
Not sure why that is. It works for me and others who have tried it. Did you try it in an incognito window? If so, that won't work if the credentials weren't previously already saved in a normal window.
Do you observe that described behavior as well, when you clear persisted credentials from your experiment?
Yes. My repro demonstrates all but step 5.
Do you object to this behavior?
I do. Specifically, this part:
b.example.com can call
navigator.credentials.get(..., mediation: optional)
--> a UI pops up to ask for confirmation to give b.example.com the credentials
In my opinion this makes it substantially easier for an attacker to phish credentials:
An attacker could, for example, craft a malicious ad that attempts to grab credentials on any site where it is displayed. The ad doesn't need to know anything about the site, because it can co-opt the browser to provide the illusion of legitimacy.
If the answer to 2. is yes or no, do we need to change the spec? Do you think we should prescribe a behavior or is it up to the browser vendor to pick one that is in line with their general behavior around password management?
I think the spec should be changed to require that browsers not allow credentials.get
of credentials not stored by calling credentials.store
.
Chrome's implementation opts all websites in. I would prefer that sites opt themselves in explicitly.
Thanks for your response. I understand your concerns now. The problem is not that credentials are passed silently (they are not) but that the user sees a confirmation dialog.
The general use case of sharing credentials across subdomains is important because we observe many cases of m.example.com and www.example.com for mobile/desktop respectively.
We could make sharing credentials across subdomains an opt-in or opt-out feature but I am not sure whether it really moves the needle. Unless I miss anything, to make a difference the following conditions need to be met:
The case where this does make a difference is if you have two subdomains with completely different login systems (separate login databases). I think this is a rare but existing case. For the CM API and the form based password manager in Chrome we chose to default to sharing credentials to subdomains that are matched by Mozilla's PSL list (https://publicsuffix.org/) and we are considering to add an opt out for users for specific domains.
@mikewest do you have an opinion w.r.t. supporting this and making it opt-in or opt-out?
Under the current implementation of Credential Management in Chrome, credentials that the user stores on a subdomain via the browser's Google Smart Lock UI are exposed programmatically to scripts running in pages on other subdomains of the site. I found this behavior surprising - in part because the blog post announcing the feature said that credentials had to be explicitly stored via
navigator.credentials.store
to be available to another subdomain vianavigator.credentials.get
. I filed a Chrome security bug that was closed WontFix.I think Chrome's implementation leans too far toward user convenience at the cost of user safety. Specifically, it significantly lowers the barrier for malicious scripts to phish credentials. Before, the script had to construct a login form that appears legitimate to the user and induce them to select their credentials to autofill (or type them in). In many cases, the form must be customized for each site. Now, the script can simply call
navigator.credentials.get
and the browser will display a UI that the user trusts. This makes it feasible, for example, to create a malicious ad that tries to grab credentials from any site it is displayed on.I suggest that the spec require a call to
navigator.credentials.store
before making a credential available vianavigator.credentials.get
on another subdomain.Reproduction steps
1) Using Chrome 57+, navigate to https://admin-dot-example.glitch.me/ 2) Enter a username and password and click 'Log in' 3) Click 'Save' to indicate you want Google Smart Lock to save your password for this site 4) Click 'Click here to repro' link 5) Click 'Sign In' to indicate you want to sign in with your account saved with Google Smart Lock 6) Observe that your username and password are displayed