Chrome自动填充/自动完成密码无值

73
当你保存了某个网站的用户名和密码后,Chrome会自动填充该用户名和密码,但是如果你尝试获取密码输入字段的值,它将是一个空字符串,即使那里确实有值******。
如果你在页面上点击任何位置,无论在哪里,都会填写input type="password的值。
这是一个示例用户/密码的HTML结构和console.log命令。虽然此处看不到,但在每个具有登录表单且在页面加载时用户名和密码被自动填充的页面上都可以重现此情况。如果在单击站点上的其他位置之前检查字段的值,它将是一个空字符串。
在Firefox或Internet Explorer中则不是这种情况,它将填充密码的input元素的值。
我使用的是Windows 7 Ultimate 64位操作系统和Google Chrome版本是48.0.2564.97 m。
这是正常行为、bug还是什么?
更新:
如果你点击F5重新加载页面并检查密码字段,密码字段的值将会出现。如果你点击Chrome左上角的刷新按钮,则密码字段的值将为空字符串。

1
正常情况下,Chrome 中保存的密码只能通过“设置”->“密码”访问,并且需要输入密码。如果密码可以被用户或 JavaScript 读取,这将是一个安全问题。 - seahorsepip
2
不是这样的,如果页面加载完成后我点击某个地方,就可以使用开发工具检查“input”元素,并查看密码字段的值。 - onetwo12
是的,你确实可以获取初始密码值,这很正常,但你无法获取用户输入的值。 - seahorsepip
1
我不知道你所说的“初始密码值”是什么意思,但如果我在加载页面并检查input type="password"字段后保存了用户/密码,我可以看到密码的值与从Chrome保存密码时相同,当你点击“为此页面保存密码”。 - onetwo12
2
我已经提交了一个问题,请求Chromium的开发人员重新考虑这个问题。如果您希望看到Chrome的行为与其他浏览器相同,请给该问题点赞。https://bugs.chromium.org/p/chromium/issues/detail?id=669724 - Ben
23个回答

61

这似乎是Chrome的一个bug。当Chrome在初始页面加载时(但不是刷新时)自动填充密码时,该值会出现在屏幕上的表单字段中,但在Javascript中查询passwordField.value会返回空字符串。如果您依赖在Javascript中看到该值,则会阻止您这样做。一旦用户在页面上执行任何其他操作,例如单击页面上的任何位置,该值突然对Javascript可见。

实际上我并不确定这是否是一个bug,或者是否存在安全原因,例如通过欺骗浏览器来填写密码,从而防止隐藏帧窃取您的密码。

我们使用的一种解决方法是检测Chrome为其自动填充的字段更改的背景颜色。Chrome将自动填充字段的背景颜色设为黄色,即使在值为空时,Javascript也始终可以看到此更改。在Javascript中检测到这一点让我们知道该字段已自动填充了一个值,尽管我们在Javascript中看到该值为空白。在我们的情况下,我们有一个登录表单,在密码字段中未填写任何内容时提交按钮是无法启用的,检测到值或自动填充背景颜色都足以确定该字段中有内容。然后我们可以启用提交按钮,并单击该按钮(或按下回车键)即可立即使密码字段值对Javascript可见,因为与页面交互可以解决该问题,所以我们可以从那里正常进行。


3
嗨,Adam。我找到了一种方法来触发Chrome将该值视为真实值,以便.value可以被访问,并将其添加为答案。这个bug没有被修复,但至少有一个解决方案。 - Kelderic
自动填充的最长时间是多少?我注意到即使是300毫秒也不足够... - Michael
我不知道自动填充所需的最长时间是否有限制,特别是当用户使用阻塞呈现浏览器扩展或者通过脚本动态注入字段到页面中时。同时,如果自动填充无法确定如何处理,例如从“其他密码”对话框中手动选择,那么用户也可能进行手动选择。为此,我使用 setInterval 对字段进行轮询,以便能够快速响应并随时监控。 - Adam Hamilton
@AdamHamilton 你好,我遇到了这个问题,我认为我们可以模拟点击/按键事件来模拟“在页面上任何地方单击”以使值对JavaScript可见。你试过这个吗? - Héctor León
在 r/web_design 上发布了一篇帖子。我很想和大家在那里讨论这个话题 https://www.reddit.com/r/web_design/comments/u3hrbz/chromium_autocompletion_lets_talk_about_it - Samuel Gfeller
显示剩余2条评论

27

2016年7月8日的有效答案

Adam正确指出这是一个漏洞(或者是预期行为)。然而,之前的回答都没有说明如何解决这个问题,因此这里提供一种方法来强制Chrome将自动填充的值视为真实值。

需要按顺序执行几件事情,而且这只需要在Chrome中运行而不是Firefox中,因此需要使用if语句。

首先我们聚焦于该元素。然后我们创建一个新的TextEvent,并运行initTextEvent,它会在值的开始添加我们指定的自定义字符串(我使用了“@@@@@”),从而触发Chrome开始像值是真实的一样工作。然后我们可以删除添加的自定义字符串,最后取消聚焦。


代码:

input.focus();

var event = document.createEvent('TextEvent');

if ( event.initTextEvent ) {

    event.initTextEvent('textInput', true, true, window, '@@@@@');

    input.dispatchEvent(event);

    input.value = input.value.replace('@@@@@','');

}

input.blur();

2016年8月10日修改:

目前此方法仅适用于 Chrome 浏览器在 Windows 和 Android 平台上。无法在 OSX 平台上使用。另外,根据以下消息,此方法将会在 2016 年 9 月停止使用:

https://www.chromestatus.com/features/5718803933560832

同时,我已经提交了一个 Chromium 报告。

https://bugs.chromium.org/p/chromium/issues/detail?id=636425

截至 8月12日,Chrome 团队的一名成员在上述报告中表示,行为不会发生变化,因为他们认为这不是一个bug。

长期解决建议:

尽管如此,当前的行为已经与最初实现时有所调整。现在用户不必与密码输入进行交互即可报告值。现在用户只需与页面的任何部分进行交互(发送鼠标或键盘事件)即可。这意味着,虽然在页面加载时运行验证仍然无法工作,但单击提交按钮将会导致 Chrome 正确报告密码值。因此,解决方法是在提交时重新验证可能自动完成的所有输入,如果您正在尝试自动完成。


2016年12月13日修改:

一个新的 Chromium 报告已经开启,并且得到了更好的接受。如果您有兴趣改变 Chrome 的这种行为,请点赞这个新报告:

https://bugs.chromium.org/p/chromium/issues/detail?id=669724


11

继续Kelderic说的,这是我的解决方法。像很多人一样,我不需要实际的密码值。我只需要知道密码框已经被自动填充,以便我可以显示正确的验证信息。

个人而言,我不会使用建议中的解决方案来检测由Chrome自动填充引起的背景颜色变化。这种方法似乎很脆弱。它依赖于黄色永远不会改变。但是这可能会因为扩展程序而被更改,并且在另一个基于Blink的浏览器(即Opera)中可能会有所不同。此外,Google未来可能会使用不同的颜色。我的方法不受样式的限制。

首先,在CSS中,当-webkit-autofil伪类应用于INPUT时,我设置了它的内容:

input:-webkit-autofill {
  content: "\feff"
}

然后,我创建了一个例行程序来检查内容是否已设置:

const autofillContent = `"${String.fromCharCode(0xFEFF)}"`;
function checkAutofill(input) {
    if (!input.value) {
        const style = window.getComputedStyle(input);
        if (style.content !== autofillContent)
            return false;
    }

    //the autofill was detected
    input.classList.add('valid'); //replace this. do want you want to the input
    return true;
}

最后,我对 input 进行了轮询,以允许自动填充完成的时间:

const input = document.querySelector("input[type=password]");

if (!checkAutofill(input)) {
    let interval = 0;
    const intervalId = setInterval(() => {
        if (checkAutofill(input) || interval++ >= 20)
            clearInterval(intervalId);
    }, 100);
}

3
我曾经采用过类似的方法,使用了一个时间间隔来触发操作,因为我发现单个的超时定时器并不总是起作用,而且如果没有必要的话,我也不想等待2秒钟。然而,我并没有设置内容,而是检查了密码字段上是否存在WebKit自动填充元素,使用 document.querySelectorAll('input[type=\"password\"]:-webkit-autofill') 进行检查。我每100毫秒检查一次,如果找到或者已经过去了2秒钟,我就会取消这个时间间隔。看起来这是我尝试过的最强大的解决方案。 - Adam Reis

8

令人惊讶的是,到2021年为止,Chrome仍然没有解决这个问题。我从2014年就遇到了自动完成问题,但现在依然没有解决。

Chrome的自动完成功能对用户来说很具有误导性。我不知道他们想要实现什么,但看起来并不好。

目前情况下,表单会显示自动完成的文本(用户/电子邮件/密码),但在后台的HTML值不在元素内。由于值不在字段中,自定义验证将禁用提交按钮。

检查字段值的脚本将显示值为空,这对用户来说更加令人困惑,因为他们可以看到文本存在,可能会认为它是有效的,导致删除一个字符并插入一个字符的困惑。(令人尴尬的是,我必须承认我不知道需要点击HTML正文的部分,所以我想知道有多少用户也不知道)

在我的例子中,我希望始终为空字段,结果发现这只是浪费时间。

如果我们尝试使用autocomplete=off,我们会发现它无法工作。为了验证字段并启用按钮,我们需要进行一些诡计。(请记住,我已经尝试过autocomplete=password new-password)和来自官方资源的其他类型的魔术诡计(链接)

最终我做到了这一点。

<script>
  $('#user').value = ' '; //one space
  $('#pass').value = ' '; // one space - if this is empty/null it will autopopulate regardless of on load event
  window.addEventListener('load', () => {
      $('#user').value = ''; // empty string
      $('#pass').value = ''; // empty string 
  });
</script>

因此,在某些情况下,密码字段中的*会闪烁一次,不太理想,但是:/ …


6
这是我对这个问题的解决方案:
$(document).ready(function(){
  if ( $("input:-webkit-autofill").length ){
    $(".error").text("Chrome autofill detected. Please click anywhere.");
  }
});

$(document).click(function(){
  $(".error").text("");
});

基本上,点击使输入对用户可见,因此我要求用户点击,当他们这样做时,我隐藏消息。

这不是最优雅的解决方案,但可能是最快的。

$(document).ready   

不应等待浏览器的自动填充,应替换为:
$(window).on("load", checkforAutoFill())

5

截至12月16日/Chrome 54的另一种选择

我无法获取密码字段的值,但在“短暂的时间”后,我可以通过选择它来获取密码的长度,这足以使我能够启用提交按钮。

setTimeout(function() {
  // get the password field
  var pwd = document.getElementById('pwd');
  pwd.focus();
  pwd.select();
  var noChars = pwd.selectionEnd;
  // move focus to username field for first-time visitors
  document.getElementById('username').focus()
  if (noChars > 0) {
    document.getElementById('loginBtn').disabled = false;
  }
}, 100);

实际上,这似乎只有偶尔起作用,可能是由于时间问题。有时报告的“noChars”值为0,有时为正值。我不得不将超时时间增加到200毫秒,以使其更加一致地工作。 - Adam Reis

4
亚当指定的解决方法是:
检测Chrome对自动填充字段所做的背景颜色更改。Chrome会将自动填充字段的背景颜色变为黄色,即使该值在Javascript中不可见,此更改始终可见于Javascript。在Javascript中检测到这一点,让我们知道该字段已被自动填充了一个值,即使在Javascript中我们看到该值为空。
我像这样做:
getComputedStyle(element).backgroundColor === "rgb(250, 255, 189)"

在Chrome中,rgb(250, 255, 189)是应用于自动填充输入框的黄色颜色。


3
我至少已经找到了一个解决这个问题的方案。
我有一个登录表单,我只想在加载后立即按下回车键,但在Chrome浏览器中遇到了密码空白问题。
以下方法似乎有效,允许最初的回车键失败,并在Chrome唤醒并提供密码值后再次重试。
$(function(){

    // bind form submit loginOnSubmit
    $('#loginForm').submit(loginOnSubmit);

    // submit form when enter pressed on username or password inputs
    $('#username,#password').keydown(function(e) {
        if (e.keyCode == 13) {
            $('#loginForm').submit(e);
            return false;
        }
    });

});

function loginOnSubmit(e, passwordRetry) {

    // on submit check if password is blank, if so run this again in 100 milliseconds
    // passwordRetry flag prevents an infinite loop
    if(password.value == "" && passwordRetry != true)
    {
        setTimeout(function(){loginOnSubmit(e,true);},100);
        return false;
    }

    // login logic here
}

3

我刚写了一个与此相关的Angular指令。最终得到了以下代码:

if ('password' == $attrs.type) {
      const _interval = $interval(() => { //interval required, chrome takes some time to autofill
          if ($element.is(':-webkit-autofill')) { //jQuery.is()
              //your code
              $interval.cancel(_interval);
          }
      }, 500, 10); //0.5s, 10 times
}

提示:它不是百分之百检测成功的,Chrome可能需要超过5秒钟才能填写输入框。


2
Chrome的预期行为是,自动填充的密码在用户与框架进行交互之前,在DOM中具有空的value值,此时Chrome实际上会填充该值。在此之前,任何客户端验证或尝试ajax提交表单的操作都将看到密码为空。
这种“在框架交互时填充密码值”的行为是不一致的。我发现,当表单托管在同源iframe中时,它只在第一次加载时运行,而在随后的加载中从不运行。
这在ajax表单上最为明显,如果自动完成的密码在第一次加载时填充,但该密码无效并且ajax提交重新呈现表单DOM,则自动完成的密码在视觉上会重新出现,但是value值永远不会填充,无论是否进行交互。
在这种情况下,没有任何解决方法可以起作用,例如触发blur或input事件。我找到的唯一解决方法是在ajax过程重新呈现表单后重置密码字段值,例如:
$('input[type="password"]').val("");

在上面的操作后,Chrome实际上会再次自动填充密码,但是value将会被填充。

在我的当前使用案例中,我正在使用ASP.NET的Ajax.BeginForm,并在AjaxOptions.OnSuccess回调函数中使用上述解决方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接