喜欢自动填充密码的浏览器你好啊

文章发布时间:

最后更新时间:

文章总字数:
2.7k

预计阅读时间:
10 分钟

页面浏览: 加载中...

快毕业了啊,思来想去还是要找一个工作,思来想去还是转前端了,这篇博客其实更适合放到别的分类里,比如碎碎念之类的。

何意味

为什么要禁用浏览器自动填充?

这个功能明明很方便啊?

OK,你是一名前端开发工程师,你们公司正在使用的一个自己开发的软件,员工的账号经常有变动的情况,比如:新入职、离职跑路等等情况啦。

为了方便不懂技术的人事可以不用写SQL来管理员工账号,自然地,你接到了一个Web前端账号管理的需求。所以,让我们理一理这个需求该怎么做。

写个登录表单

首先,为了防止普通员工将自己修改成公司老板,我们需要鉴权机制,以及一个登录入口。

让我们写一个简单的登录表单。

1
2
3
4
5
6
7
<form action="/login" method="post">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required>
<label for="password">密码:</label>
<input type="password" id="password" name="password" required>
<input type="submit" value="登录">
</form>

这样我们就可以愉快地交给后端写鉴权逻辑了(还是得自己写)!

开始管理账号!

接下来,让我们写个接口,从后端获取账号列表。

1
2
3
fetch('/api/GeiWoZhangHaoLieBiao')
.then(response => response.json())
.then(data => accountList.value = data);

然后?大概是用某个UI库的表格组件,展示账号列表(自己写是不可能的)。

然后?大概就是CRUD的无聊样板代码了,为了实现CRUDC,你写了个增加账号的表单。

1
2
3
4
5
6
7
8
9
<form action="/createAccount" method="post">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required>
<label for="password">密码:</label>
<input type="password" id="password" name="password" required>
<label for="confirmPassword">确认密码:</label>
<input type="password" id="confirmPassword" name="confirmPassword" required>
<input type="submit" value="保存">
</form>

做完这些后,你自信满满地通知产品,账号管理功能已经实现了。于是经过了一些繁琐的流程,账号管理终于在你司投入使用了。好啊!可喜可贺!可喜可贺!可喜可贺啊!

不懂技术的人事

某天,不懂技术的人事,一脸深沉地来问你:“为什么我添加新账号总是提示我两次输入密码不同?”

面对这个在21世纪还在使用二指禅打字对电脑一窍不通的人事,你本能地先怀疑了她而没有怀疑自己的问题。

—— 密码真的输对了吗?

—— 你截个图给我看看。

—— 截图键在哪里?

—— 微信请用Alt+A,QQ请用Ctrl+Alt+A……

—— 找不到Alt键在哪里?

—— 算了,你等会,我去找你。

不懂技术的人事挪动着她的鼠标,点开了一个添加账号的面板。

你目瞪口呆地看着她的电脑弹出添加账号面板,
自动填充了一个新账号的用户名和密码,
直接无视了前面的用户名和密码,
把自认为的密码输入了确认密码中。
当两次输入密码不同的红色提示出现时,
她回头,一脸森陈地看向了你。

深沉.png

你力竭,告诉她这是一个浏览器自动填充的功能,账号和密码需要你自己设定。如有需要可以把浏览器的自动填充功能关闭。
她高兴,又一个难题解决了。

回工位的路上,或许是灵光一闪,抑或是早上吃的火腿扒早安营养卷,在肚子里翻了个身,让你香汗淋漓了。你突然想到了,浏览器自动填充不应该出现在这个场景下

麦麦的火腿扒早安营养卷其实还是很好吃的,并没有让我感到不舒服。

纳尼纳尼

解释原理咯

自动填充是怎么工作的

诸如Chrome,Edge等浏览器,都有自动填充密码的功能。首先,浏览器会把密码信息存在你的电脑中(大概率是)

关于密码存储

大部分浏览器都是声称把密码信息存在本地数据库中的,至少Chrome,Edge,FireFox都是这么说的。浏览器不会明文存储密码,而是使用操作系统提供的加密API,也就是操作系统级的加密

怎么加密?怎么解密?

或许会比较复杂,也有可能和上一期异或加密有关,具体等我了解了再谈罢。

当页面加载时,浏览器会查找<input type="password">字段。接下来,浏览器会检查当前网址与保存密码时的域名是否匹配,当用户点击用户名输入框或页面加载完成时,浏览器会查询本地数据库,找到匹配的账号记录,将账号填入对应字段。

关闭自动填充?

倘若想从浏览器层面关闭自动填充的话,似乎不简单。Chrome按照下图的流程大概就可以关闭自动填充。

graph TB A[密码和自动填充] --> B[Google 密码管理工具] B --> C{关闭方式} C -->|不再询问是否保存密码| D[设置] D --> E[提示保存密码和通行密钥] C -->|删除密码| F{怎么删除} F -->|删除部分密码| G[密码] F -->|删除所有密码| H[设置] H --> I[删除所有 Google 密码管理工具数据] C -->|关闭自动登录| J[设置] J --> K[自动登录]

可见Chrome的方案并不是很优雅,也许在一些场景用户会希望浏览器自动填充密码,但是在开发时,总是难以避免一个页面同时出现两个密码输入框的情况。那么有没有更好的方案了?每个用户的浏览器都不一样,难道每个用户都要手动关闭自动填充功能吗?

其他的浏览器?

幸运的是Edge可以很方便地直接选择关闭自动填充密码和通行密钥不幸的是,Edge依然会直接关闭所有的密码自动填充功能。

解决方案!

其实并不优雅

骗过浏览器

首先自然地想到,浏览器查找的是<input type="password">字段,那么我改成<input type="text">的写法不就行了?至于密码掩码,我可以在前端自己实现。

好吧,那么让我们这么写:

1
2
<label for="password">密码:</label>
<input type="text" id="password" name="password" required>

很遗憾,这样是不行的,由于idname属性里都包含了password,所以浏览器依然会把这个字段也作为密码字段去自动填充。

再骗!

既然这样的话,让我们试试直接去password化:

1
2
<label for="mima">这不是密码哦</label>
<input type="text" id="mima" name="mima" required>

很遗憾,大部分浏览器会使用机器学习模型识别登录表单和启发式规则去检查表单结构、字段名称。比如secretpasswd,甚至nameidlabel等属性都会被用于推测密码字段,即使字段名称被混淆也能识别。

令人感慨的是为了让用户能够方便地登录,浏览器确实花了很大功夫去检查表单结构、字段名称。所以这就是Edge吃我内存的原因吗?

并不把开发者当人的开发者模式

查阅资料后发现,网站开发者可以通过HTML属性控制自动填充行为:

1
2
3
4
<form autocomplete="off">
<input type="text" name="username" autocomplete="off">
<input type="password" name="password" autocomplete="off">
</form>

然而,某人尝试之后发现这并不起效,autocomplete="off" 对密码字段的效果因浏览器而异,ChromeEdge通常会忽略此设置以保障用户体验。

能不能尊重一下HTML啊喂

李代桃僵

事实上,在写我自己的项目时,我使用的字段名就是pilot_password,但是依然没有逃过浏览器大人的制裁啊。遇到这一步时,也确实被烦恼了好久啊,最后用了个不太优雅的方式才解决了这个问题,那就是写一个隐藏的欺骗字段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div style="display: none;">
<input type="text" name="fake-username">
<input type="password" name="fake-password">
</div>
<!-- 瞒天过海咯 -->
<form action="/createAccount" method="post">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required>
<label for="password">密码:</label>
<input type="password" id="password" name="password" required>
<label for="confirmPassword">确认密码:</label>
<input type="password" id="confirmPassword" name="confirmPassword" required>
<input type="submit" value="保存">
</form>

原理就是浏览器的识别行为是从上到下,识别到一个字段,就会自动填充密码。那么接下来的真正密码字段,就会被浏览器忽略了。

Conclusion

作为恢复更新的第一篇博客,其实并没有做到特别让我满意的地步。令人遗憾的是找遍了社区问遍了AI都没有找到一个真正优雅的解决方案,作为一个还是有那么一点在意自己码风的coder,闲暇时总是会对着自己的代码里这么两个多余的无用的空白元素发呆。在一个注重用户体验的时代,各家浏览器并没有把开发者当人,也没有把用户当人。也许会有哪天在输入验证码时突然弹出来几条曾经的验证码选项,也许会在输入地点信息时被自作主张地填充了早就不使用了的地址,也许会在测试自己做出来的有自动填充功能的输入框时被浏览器的自动填充的建议列表挡住。

下一篇会是Cesium的地形渲染,偏硬核,应该还会涉及一些Unity3D或者Unreal Engine,具体选哪个看我先学会哪个罢。尽量在这个月内发。