How do I get password fields to be autofilled with

2020-04-18 04:57发布

问题:

If I inspect an <input type="password"/> from my Tampermonkey script and observe changes using a change handler for a password field that is filled by the Password Manager feature of Chrome, the value remains empty until I physically perform a real click in the page. I have tried in my script clicking in the page, but Chrome knows it’s not a real click. When I physically click in the page, the change event suddenly fires, the password field gets a value, and then my script reacts properly (due to having the change event). But I wrote this script to avoid having to interact with the page in the first place, so this defeats the point of my script.

Is there a way to get TamperMonkey to mark the page as having had the user interact with it (e.g., some hypothetical GM_setUserTouched() or GM_autoFillPasswords() API) so that the Password Manager feature actually fills the <input type="password"/> in without requiring me to click in the page?

Background

In Chrome, this behavior is documented in #352527 comment 15 where it is unexpected behavior to the reporter and #398805 where there is a case that Chrome fails to implement the behavior I don’t want. It is considered a feature that when autofill and Chrome’s built-in Password Manager fill out a form, password characters are displayed to the user in the password field, but the DOM HTMLInputElement.value is set to "". When the user interacts with the page, such as by clicking in it or pressing a key, Chrome modifies HTMLInputElement.value to contain the password and a change event is fired at the element. The cited reason for doing this is “security reasons” (e.g., if a website script was reading from the password element, it only would have a chance to do so if the user was looking at the page… so popunders or non-visible frames wouldn’t be able to do it or something? I’m not sure what this protects you from: once the user interacts with the page, all of the scripts would have access to the password anyway. And if bad scripts are being served from the same origin as the <input type="password"/>, the website itself has a security flaw, not Chrome…).

Greasemonkey historically has helper APIs and a @grant system to enable the userscript to work around issues like this. Edit: when creating the repro (below), I discovered that Firefox makes the autofill password available to the DOM without waiting for the user to interact with the window. Thus, Greasemonkey doesn’t need a GM_forceAutofill() API because Firefox doesn’t exhibit this Chrome quirk. As a result, Tampermonkey doesn’t have such an API.

Repro

Because people do not believe me when I describe the behavior exhibited by Chrome, I have prepared a repro. It takes some work to get Chrome into a state where it doesn’t think the user has interacted with the page yet, but you should be able to see what I see using these steps:

  1. Open Chrome. I am using Chrome 61.0.3163.91 64-bit on Windows 10.
  2. Navigate to https://fiddle.jshell.net/xqfynp3e/22/show/light/
  3. Enter some bogus username and password and hit enter or click the button. Chrome should prompt you to save the password.
  4. Save the password.
  5. Open Developer tools.
  6. Enter this into Console (to navigate to the page without accidentally interacting with it): window.location.href = 'https://fiddle.jshell.net/xqfynp3e/22/show/light/?1'
  7. Run document.querySelector('input[type=password]').value in Console.
  8. Observe that the form’s password appears to be filled in visually and yet reading the DOM element in Console yields "".
  9. Click in the document.
  10. Run document.querySelector('input[type=password]').value in Console again.
  11. Observe that the form’s password hasn’t changed appearance and yet reading the DOM element in Console yields the bogus password you saved.

My question, restated: how can I get Tampermonkey to perform the “Click in the document” step? How do I tell Chrome’s password auto-filler that I interacted with the page without actually physically interacting with the page?

EDIT: I have found an alternative way to securely store passwords in Chrome and access them via userscripts by using the Credentials Web API’s silent mediation support: https://imgur.com/a/ts2W1

回答1:

You cannot do this using Google's built-in password storage because, as you stated yourself, Chrome requires user interaction to enable such passwords -- as a security feature.

Specifically, Chrome requires an event with the isTrusted property set to true. Tampermonkey cannot work around this because even Chrome extensions are not able to set the isTrusted property.

See also, this related feature request from 2015.

One solution is to use a password manager that fills these fields without using Google's built-in storage.
There are many available, with varying degrees of cross-device, and cross-browser, support.

Or you can write your own Tampermonkey script to fill in these fields, irregardless of what Chrome has stored.

If you do write a Tampermonkey script, I recommend that you use a secure storage framework, don't hardcode login info into the script.