如何防止浏览器存储密码

103

我需要阻止浏览器存储用户名和密码,因为我正在开发一个包含更加安全数据的网络应用。我的客户要求我这样做。

我尝试在HTML表单和密码框中使用autocomplete="off"属性。但是在最新的浏览器(例如Chrome 55、Firefox 38+、Internet Explorer 11等)中,这并不起作用。

有没有更好的解决方案?


1
我有一个类似的情况 - 一个内部网页应用程序,其中有一个仅支持技术人员的区域,密码可以解锁应用程序的不安全功能。允许最终用户访问这个区域将非常糟糕,因此确实存在一些情况,您不希望保存密码。顺便说一下,我仍在寻找一个可行的答案。 - semmelbroesel
2
我真的不建议人们在2020年这样做,除非你有非常特殊的情况。这会使安全性变得更糟。用户应该为他们使用的每个不同服务使用唯一的密码。当然,没有办法记住如此多的唯一和不相关的密码,所以用户需要使用密码管理器来完成这项工作。所有大型浏览器都内置了密码管理器,并且有很多第三方选项。除了一些特殊情况的例外,阻止密码管理器很可能会对您的安全性造成更多伤害而不是帮助。 - Chris
https://github.com/noppa/text-security - But those new buttons though..
1
@Chris - 正是因为“特殊情况异常”,所以确实应该有一种适当的方法来解决这个问题。那些罕见的情况,整个重点就在于确认用户仍然是同一个用户,或者当他们更改密码时,你作为开发人员知道(必须)保存的值是错误的。 - Morvael
对于遵循最佳实践并使用密码管理器的用户,我不认为禁用自动完成会确认他们是同一用户?至少在提示要求输入当前密码时。但我完全同意有些情况下你想避免自动填充,只是我认为这种情况相当罕见。比如更改密码时,你不希望新密码字段自动填充旧密码。(但我认为如果你将输入字段的名称设置为“new_password”之类的内容,像Chrome或1P这样的好的密码管理器就会知道不要自动填充旧密码) - Chris
显示剩余2条评论
25个回答

71

感谢您回复我的邮件。我按照下面的链接进行了操作:

禁用浏览器的“保存密码”功能

我解决了问题,只需在输入框中添加readonlyonfocus="this.removeAttribute('readonly');" 属性,同时保留autocomplete="off"属性,如下所示。

<input type="text" name="UserName" autocomplete="off" readonly 
onfocus="this.removeAttribute('readonly');" >

<input type="password" name="Password" autocomplete="off" readonly 
onfocus="this.removeAttribute('readonly');" >

这对我来说很好用。


浏览器不再支持“autocomplete”属性。 - csandreas1
4
尝试过了,也不行,特别是火狐开发版。 - Alexander
我的解决方案有点取巧,但我使用JS来清除该字段。 - David Moritz
在我的登录页面(MVC项目)中,我添加了以下内容: onfocus ="this.removeAttribute('readonly');", onfocusout ="this.setAttribute('readonly','readonly');" 它的效果非常好!即使在FireFox 67.0.4 (64位)中也能正常工作!谢谢! - Developer
1
在 Firefox 浏览器版本 84.0b8(64 位)中无法运行。 - Ajay Takur
1
这个方法破坏了iOS设备上的键盘。在iPhone上没有显示键盘。 - Niklas

41
尝试防止浏览器存储密码并不是推荐的做法。虽然有一些变通方法可以实现,但是现代浏览器没有默认提供此功能,这是有充分理由的。现代浏览器将密码存储在密码管理器中,以使用户可以使用比平时更强的密码。
正如MDN:如何关闭表单自动完成所解释的那样:
现代浏览器实现了集成的密码管理功能:当用户为一个网站输入用户名和密码时,浏览器会提示用户是否记住这些信息。当用户再次访问该网站时,浏览器将自动填充存储的值到登录字段中。此外,浏览器允许用户选择主密码,用于加密存储的登录详情。即使没有主密码,浏览器内置的密码管理通常被认为是安全性的净收益。因为用户不必记住浏览器为他们存储的密码,他们能够选择比平常更强的密码。因此,许多现代浏览器不支持在登录字段上使用autocomplete="off",因为如果一个网站设置表单的autocomplete="off",并且表单包含用户名和密码输入字段,则浏览器仍然会提供记住这个登录信息的选项,并且如果用户同意,浏览器将在用户下次访问页面时自动填充这些字段。如果一个网站设置了用户名和密码输入字段的autocomplete="off",则浏览器仍然会提供记住该登录信息的选项,并且如果用户同意,浏览器将在用户下次访问页面时自动填充这些字段。这是Firefox(自版本38)、Google Chrome(自34版本)和Internet Explorer(自11版本)的行为。如果作者希望防止用户管理页面中的密码字段自动填充,可以指定autocomplete="new-password",但并非所有浏览器都支持此功能。

5
在浏览器中揭示密码非常容易。只需打开开发者工具,将输入类型从密码更改为文本,就可以轻松揭示密码!是的,就是这么简单。 - Elroy Flynn
27
有些情况下使用密码管理器是没有意义的,比如更改密码。autocomplete="new-password" 在我的 Firefox 55.0.3 x64 上无法使用。请为我进行翻译并使内容更易懂,但不要改变原意。 - Pancho
11
在Chrome 65中,autocomplete="new-password"的作用与我想要的完全相反。它不会显示“是否要更新已保存密码”的对话框...它只会默默地更新我的存储密码。比无用更糟糕 - 有危险!>:( - Doug McLean
1
这本来是一个好答案,但通常在浏览器中查看以明文保存的密码是一件微不足道的事情。如果所有现代浏览器都要求输入密码才能解锁已保存的密码,并使用合理强度的加密保存密码,那么你可以考虑让浏览器为你保存密码。 - UncaAlby
5
如果你正在开发和管理支持设置密码字段的用户界面,那么强制要求这样做是否好的语义学意义完全没有意义!你认为强制浏览器让管理员在其浏览器中保存创建的每个用户的每个密码更安全吗?!每次我读到“净增益”评论时,我内心都会有些沮丧。 - dubmojo
显示剩余11条评论

29

这里是一个纯HTML/CSS解决方案,在Chrome中经过65.0.3325.162版本(官方构建)(64位)的测试。

将输入框设置为type="text",并使用CSStext-security:disc来模拟type="password"

<input type="text" name="username">
<input type="text" name="password" style="text-security:disc; -webkit-text-security:disc;" autocomplete="off">

上述来源包含了关于CSS moz-text-security-webkit-text-security属性的解决方法。

来源:https://github.com/kylewelsby/dotsfont

就我所测试的情况来看,这个解决方案适用于ChromeFirefox版本59.0(64位)Internet Explorer版本11.0.9600以及IE模拟器Internet Explorer 5及更高版本。 安全编辑2023:我已经给密码输入框添加了属性autocomplete="off"。通过设置autocomplete="off",您指示浏览器不要为该特定输入字段填充或记住任何值。
需要注意的是,尽管大多数现代浏览器都会尊重autocomplete="off"属性,但出于安全原因,某些浏览器可能会忽略它或提供用户覆盖设置的选项。此外,不同的浏览器可能会对该属性进行不同的解释,因此它并非绝对可靠的解决方案。

4
考虑到密码框具有一些额外的安全属性——比如无法复制粘贴其中的文本,因此这可能不是一个好主意。 - tXK
1
@tXK 我同意你的看法...但是你可以通过一个小技巧复制/粘贴密码字段内的文本,例如在Chrome中,右键单击密码字段,进入检查元素,然后将 type="password" 更改为 type="text"。 - Natro90
4
在密码字段中,复制/粘贴不应被认为是一个问题,因为这是密码管理器的理论基础。可以询问Troy Hunt。 - Lorenzo Peña
我不认为这是一个好主意。例如,如果我们有自动填充或密码管理器扩展,它如何识别密码字段,可能有一些选项可以找到它...但这对于初学者来说不是一个好的教训。 - Khurram Shaikh
1
这是我在使用Chrome浏览器时,在React上唯一有效的解决方案。 - Luis Febro
1
不要这样做!这会禁用密码输入字段具有的特殊安全行为。浏览器和操作系统在处理密码输入时会格外小心。在 macOS 上,当您使用密码字段时,Chrome 将其标记为“安全输入”,以保护具有正常输入监视权限的应用程序无法读取它。而且,由于浏览器认为它是无聊的文本,它可能会将该值存储以供以后自动完成,但它不会以额外的安全方式存储该值。Chrome 的自动填充存储了很多信息,您可以在 Web Data sqlite 数据库中的 autofill 表中找到。 - Chris

22

我通过在密码输入框中添加autocomplete="one-time-code"解决了此问题。

根据HTML参考文档autocomplete="one-time-code"是用于验证用户身份的一次性代码。它似乎是最适合的选择。


3
奇怪,MDN建议使用new-password(https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion#preventing_autofilling_with_autocompletenew-password),但是在火狐浏览器中对我都不起作用。 - Christian Gollhardt
1
这在某种程度上是有效的,只要密码不会自动放入输入框中。然而,Firefox在提交表单时会询问是否存储密码。 - DrMarbuse
“一次性验证码”在Firefox上无法使用。 - Serhii Holinei

12

你应该能够制作一个假的隐藏密码框来防止它。

<form>
  <div style="display:none">
    <input type="password" tabindex="-1"/>
  </div>
  <input type="text" name="username" placeholder="username"/>
  <input type="password" name="password" placeholder="password"/>
</form>


太聪明了! :) 不过我会给它一个值,比如value="x"。而且我也不会使用display:none,而是将其放在窗口之外的荒谬位置或者使用负的z-index。 - FlorianB
<input type="hidden"> 是什么? - qd0r
3
在 Firefox 84.0b5 (64 位) 浏览器版本中,它不能正常工作。 - Ajay Takur

7

默认情况下,没有任何正确的方法可以禁止在您的浏览器中保存密码。但是,幸运的是,有一个解决方法几乎适用于所有浏览器。

为了实现这一点,在实际输入框之前添加一个虚拟输入框并设置autocomplete="off"以及一些自定义样式来隐藏它并提供tabIndex。

一些浏览器(Chrome)的自动填充功能将填充它发现的第一个密码输入框和其之前的输入框。因此,通过这个技巧,它只会填充一个不重要的不可见输入框。

          <div className="password-input">
            <input
              type="password"
              id="prevent_autofill"
              autoComplete="off"
              style={{
                opacity: '0',
                position: 'absolute',
                height: '0',
                width: '0',
                padding: '0',
                margin: '0'
              }}
              tabIndex="-2"
            />
            <input
              type="password"
              autoComplete="off"
              className="password-input-box"
              placeholder="Password"
              onChange={e => this.handleChange(e, 'password')}
            />
          </div>

3

在发布此帖子时,之前的回答都不适用于我。

这种方法使用可见密码字段从用户获取密码,并使用隐藏密码字段将密码传递到服务器。在提交表单之前,将清空可见密码字段,但不使用表单submit事件处理程序(请参见下一段解释)。该方法尽快将可见密码字段值传输到隐藏密码字段(无需额外开销),然后清空可见密码字段。如果用户切换回可见密码字段,则会恢复值。它使用占位符在字段被擦除后显示●●●。

我尝试在表单onsubmit事件上清除可见密码字段,但浏览器似乎在事件处理程序之前检查值,并提示用户保存密码。实际上,如果取消注释passwordchange末尾的alert,浏览器仍将提示保存密码。

function formsubmit(e) {
  document.getElementById('form_password').setAttribute('placeholder', 'password');
}

function userinputfocus(e) {
  //Just to make the browser mark the username field as required
  // like the password field does.
  e.target.value = e.target.value;
}

function passwordfocus(e) {
  e.target.setAttribute('placeholder', 'password');
  e.target.setAttribute('required', 'required');
  e.target.value = document.getElementById('password').value;
}

function passwordkeydown(e) {
  if (e.key === 'Enter') {
    passwordchange(e.target);
  }
}

function passwordblur(e) {
  passwordchange(e.target);

  if (document.getElementById('password').value !== '') {
    var placeholder = '';
      
    for (i = 0; i < document.getElementById('password').value.length; i++) {
      placeholder = placeholder + '●';
    }
      
    document.getElementById('form_password').setAttribute('placeholder', placeholder);
  } else {
    document.getElementById('form_password').setAttribute('placeholder', 'password');
  }
}

function passwordchange(password) {
  if (password.getAttribute('placeholder') === 'password') {
    if (password.value === '') {
      password.setAttribute('required', 'required');
    } else {
      password.removeAttribute('required');
      var placeholder = '';

      for (i = 0; i < password.value.length; i++) {
        placeholder = placeholder + '●';
      }
    }

    document.getElementById('password').value = password.value;
    password.value = '';

    //This alert will make the browser prompt for a password save
    //alert(e.type);
  }
}
#form_password:not([placeholder='password'])::placeholder {
  color: red; /*change to black*/
  opacity: 1;
}
<form onsubmit="formsubmit(event)" action="/action_page.php">
<input type="hidden" id="password" name="password" />

<input type="text" id="username" name="username" required
  autocomplete="off" placeholder="username"
  onfocus="userinputfocus(event)" />
<input type="password" id="form_password" name="form_password" required
  autocomplete="off" placeholder="password"
  onfocus="passwordfocus(event)"
  onkeydown="passwordkeydown(event)"
  onblur="passwordblur(event)"/>
<br />
<input type="submit"/>


3

我尝试了许多解决方案,最终采用了这个解决方案。

HTML代码

<input type="text" name="UserName" id="UserName" placeholder="UserName" autocomplete="off" />
<input type="text" name="Password" id="Password" placeholder="Password" autocomplete="off"/>

CSS 代码

#Password {
    text-security: disc;
    -webkit-text-security: disc;
    -moz-text-security: disc;
}

JavaScript 代码

window.onload = function () {
    init();
}

function init() {
    var x = document.getElementsByTagName("input")["Password"];
    var style = window.getComputedStyle(x);
    console.log(style);

    if (style.webkitTextSecurity) {
        // Do nothing
    } else {
        x.setAttribute("type", "password");
    }
}

请注意,这在许多浏览器中不受支持。这些浏览器将只会以明文形式显示输入的密码。 - JPortillo
@JPortillo 只有两个浏览器不受支持:Firefox和Internet Explorer。 - MilMike

1

以下是一个纯html/css的解决方案(没有js)

<textarea required="required" autocorrect="off" autocapitalize="off" name="username" class="form-control" placeholder="Your username"  rows="1" cols="20" wrap="off"></textarea>
<textarea required="required" autocorrect="off" autocapitalize="off" name="password" class="form-control password" placeholder="Your password"  rows="1" cols="20" wrap="off"></textarea>

@font-face {
  font-family: 'password';
  src: url('css/font/password.woff2') format('woff2'),
       url('css/font/password.woff') format('woff'),
       url('css/font/password.ttf') format('truetype');
  font-weight: normal;
  font-style: normal;
}

textarea.form-control {
  overflow:hidden;
  resize:none;
  height:34px;
}

textarea.form-control.password:valid {
  font-family: 'password';
}

注意事项

  1. Textarea输入框防止自动填充和密码管理器触发
  2. wrap=off/overflow=hidden/rows=1 强制单行显示
  3. 使用required伪类css让占位符生效
  4. 可能需要一些“阻止事件键=13 / 提交”处理
  5. 在firefox/chrome/iOS中运行正常

最终,这变成了一个令人发指的网络垃圾的hack集合(但它能用)


这里的问题在于浏览器和操作系统不知道应该把这个字段作为安全密码输入进行处理。因此,任何可以访问剪贴板的东西都可以复制并读取它,由于它不被视为安全输入,因此键盘监控程序可以更轻松地读取它,或者任何具有屏幕阅读器/辅助功能访问权限的人都可以读取它等等。 实际的密码字段具有许多理想的安全属性。 - Chris

1
< input type="password" style='pointer-event: none' onInput= (e) => handleInput(e) />

function handleInput(e) {
  e.preventDefault();
  e.stopPropagation();
  e.target.setAttribute('readonly', true);
  setTimeout(() => {
    e.target.focus();
    e.target.removeAttribute('readonly');
  });
}

需要解释一下。 - Peter Mortensen

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