别再纠结前端要不要提交明文口令,浏览器已经内置非常好的方案

165 天前
 iqoo

这个十年前就有结论的问题每隔一段时间都会有人讨论。结论是不管什么环境,前端提交 hash 后的口令总是好的,防的不是中途的嗅探者,而是脱库后的破解者。前端 hash 越耗时,脱库后跑字典越慢。

至于前端 hash 也不用自己捣鼓 js/wasm 这些,主流浏览器早已内置 PBKDF2 算法,较新的 CPU 都有相应的硬件加速,比自己实现可以快很多倍。

演示:

const username = new TextEncoder().encode('alice')
const password = new TextEncoder().encode('hello1234')

// 重复 1000 万次 SHA256
const pbkdfOpts = {
  name: 'PBKDF2',
  hash: 'SHA-256',
  salt: username,
  iterations: 1e7,
}

async function pbkdf2(pwd, opts, bits) {
  const baseKey = await crypto.subtle.importKey('raw', pwd, 'PBKDF2', false, ['deriveBits'])
  const buf = await crypto.subtle.deriveBits(opts, baseKey, bits)
  return new Uint8Array(buf)
}

const dk = await pbkdf2(password, pbkdfOpts, 256)

// 注册/登录提交 dk 即可,无需提交 password
console.log(dk)

10870 次点击
所在节点    程序员
93 条回复
zictos
165 天前
先 sha256 ,如果还不行就加时间戳再 sha256 一次,时间戳也作为参数传输
iqoo
165 天前
@Chad0000 记录每个用户的难度值就可以了(这个值公开)。登录时输完用户名,前端可以得到当前难度值。然后算这个难度值的 dk 。如果后端还给了新的难度值,顺便再算新的 dk 。
Chad0000
165 天前
@iqoo #22

又多一个难度值的概念,请问你做了这么多操作,将安全提升了多少?有 100%提升还是 1%还不到?
iqoo
165 天前
@Chad0000 难度值本身就有,代码里那个 1000 万。你说要考虑后期升级,才出现这么多操作。最简单就是用固定的值。
Chad0000
165 天前
@iqoo #24

那你的方案会带来密码碎片化:只要无法做到所有终端一起升级,服务器就不得不使用原密码将每种 Hash 都算一遍保存起来,否则还没来得及升级的终端的登录将无法得到验证。
Chad0000
165 天前
@iqoo #24

或者,你的方案本来执行得好好的,哪天老板脑子抽筋说某个穿戴设备也需要对接。

手表上跑 1000 万次 Sha256 ,额。
iqoo
165 天前
@Chad0000 不会有碎片化。保存的只有当前难度值和对应的 hash 。客户端升级的时候需要用户重新输入一次密码。升级难度也不会很频繁,可能几年才一次。
iqoo
165 天前
@Chad0000 低端设备确实没办法😂 不过穿戴设备也不方便输入,可以直接在手机或电脑上登录,把登录态同步过来。或者通过扫描等方式把 dk 同步过来。
shiny
165 天前
前端能 hash ,后端也能 hash
大多数情况下脱库都是查彩虹表,这种场景下一个靠谱的 hash 才是关键,而不是在哪一端 hash 的。
Chad0000
165 天前
@iqoo #27
你没明白我说的,我说的就是升级的时候发生的事情:只要你没办法统一升级客户端(让他们一致使用新的难度),你就得保存难度的 Hash 过后的密码,否则其他难度的登录请求你没办法验证。
iqoo
165 天前
@shiny hash 和 PBKDF 还是有区别的,这个强度的 PBKDF 彩虹表得几十年吧。
tool2dx
165 天前
这算法自古以来就有,早在 linux 还叫 unix 年代,password 就为了防止本地账号密码破解,启用了递归 hash 算法,那时候是 md5 递归 1000 次,代码里还专门注明了一句:just to make sure things don't run too fast 。
mark2025
165 天前
PBKDF2 挺不错的,基本是通用口令散列算法了
shiny
165 天前
@iqoo 这也是种 hash 算法,所以我说选个靠谱的 hash 算法……
iqoo
165 天前
@Chad0000 难度值可以不用写死在客户端,可以通过用户名实时查询,是公开的。(这个值也没有太大的隐私性)
iqoo
165 天前
@shiny 目前浏览器里只有 sha 相关的 pbkdf ,只能在 salt 上做些变通,比如选取 网站 uuid+用户名,专为这个网站做彩虹表,成本太大划不来。
Chad0000
165 天前
@iqoo #35

用户名可以被扫描了:安全值 - 1 。

另,哪天 Sha256 不安全了,你需要换个算法,你一样会遇到上述问题。你为了避免被脱库,将算法固化在了前端,导致后期修改比较复杂,这个算是得不偿失的方案。
drymonfidelia
165 天前
@iqoo 穿戴设备也不方便输入:老板说别人都可以,我们不行就是你不专业
tool2dx
165 天前
@Chad0000 没有固化,现在的 hash 和以前的 hash 不一样了,都是加密码的。

比如 wireguard 协议,里面用的是 blake2s 算法,和 OP 这个类似,你密码一变,hash 结果天差地别。
forvvvv123
165 天前
@lovelylain

感觉不好搞吧,前端 RSA 或者 ECC 把密码密文传过来,后端解成明文后 hash 再跟库里的 hash 对比吗? 这样其实后端开发能知道用户所有明文密码

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1044263

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX