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

35 天前
 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)

9459 次点击
所在节点    程序员
93 条回复
Chad0000
35 天前
@tool2dx #39
“现在的 hash 和以前的 hash 不一样了"

你看吧,Hash 也有新算法,也就是说你永远没办法保证现在的 Hash 算法就是最佳的:这就意味着哪天你会需要升级前端算法以保证安全(不会被迅速脱库)。又回到我上面说的:如何确保不同终端一并更新,而且你的新算法还得考虑到不同算力的终端(从 PC 到手机再到穿戴设备)。而且你的前端算法还是公开的。

所以,为什么不定期更新后端密码落库的算法呢?这个百分百可控,不影响前端,任你随便玩,你还可以将应用和 DB 分开,这样只拿到 DB 不知道应用里的参数(比如这个 1000 万次 Hash ),是没办法彩虹的。不更好么?
iqoo
35 天前
@Chad0000 这个是可以缓解的,不管用户存不存在,都可以返回一个值。

实际应用时为了灵活性,返回的并不是难度值,而是算法名和参数,这样适用于任何算法。

(更进一步,比如针对浏览器的场合,返回的甚至是一个 JS 版本号,前端动态加载这个 JS ,然后通过约定的接口去计算 hash 。顺便还可以在动态模块里做些风控检测)
Chad0000
35 天前
@iqoo #42
那问题可就多了去了,不同终端还得适配不对算法:我桌面软件不是 Jscript 写的怎么办?手表也不可能是 JS 写的,要动态加载 JS ?

如何确保这个 JS 是安全的,万一被别人替换了或开发不小心搞错配置文件直接改明文了。你返回算法名,如果我旧的终端当时还没有这种新的算法呢?那你得提前更新新算法到终端,否则终端不支持。

还是回到那句话:你做了这么复杂的方案,安全提升了(或降低)几个百分点?是否值得?
treblex
35 天前
@Chad0000 #16 django 是这样支持不同的加密后端的,当然我也是不支持在前端搞这个的
tool2dx
35 天前
我简单科普一下,可能有些新人不了解 PBKDF2

一个 hash 函数,比如 sha256 为了防止被破解,需要加入密码,起名为 HMAC 。

而 PBKDF2 ,就是针对于 HMAC 递归计算,次数就是 OP 里的 1000 万次,计算量的增加,大大加强了 HMAC 的安全性。

而网页登陆的用户名,就是 hash 函数的密码(也叫 salt)

这样做有什么好处呢,那就是针对用户名 A 建立的彩虹表,无法用于用户名 B/C/D 。不同用户名只能单独计算,这就增加了密码防撞的安全性。
iqoo
35 天前
@Chad0000 这个方案只讨论针对浏览器的场合,本身就是一个低成本方案,也没考虑要升级难度。多加几行代码,多花几百 ms 强化口令,用 dk 代替 password 而已,没有非常大的提升但聊胜于无。
rxmt
35 天前
前几天刷到好几个明文密码帖子,我没有仔细研究过密码传输,正在看各个帖子里的观点。
---

我有一个想法,如果非对称加密传输可以么?不这样做的原因是开销太大吗?
Chad0000
35 天前
@rxmt #47
有人问得好,安全要看你防的谁。

非对称你是要防传输层:
- 人家大可以给你一个假的密钥:你可以做证书认证 - 恭喜你发明了 HTTPS
- 传输层大可以直接换掉你的 JS 文件,跳过加密算法不更简单么
rxmt
35 天前
@Chad0000 我有一个十几个人用的工具是这么干的:
前端公钥加密 -> 后端解密 -> 对比鉴权

好像这样做没有跳过加密的问题,但是好像完全不在乎开销= =

系统学学这块好像还挺有意思
zhtyytg
35 天前
@rxmt 是没有必要,前端加密对于本地用户仅仅只是增加了破解难度,对于中间人攻击则 https 就可以防御,对于后端来说如果拿到即存数据库则密码是不安全的所以后端往往会在存储前再处理一次(不可逆 hash)。所以前端加密的意义在资深前端看来仅仅起到防脚本小子和呆比,类比前端代码混淆
gamexg
35 天前
@rxmt #47 因为 https 已经实现了非对称加密握手+对称加密传输.
自己再做一层没必要,而且还不如 https 实现可靠,至少 https 还有个根证书机构认证公钥.
gamexg
35 天前
我没理解错误的话,
这个的作用是把用户长度不一,特殊字符不一的密码统一变成了 17EB4014C8C461C300E9B61518B9A18B 固定长度固定格式的密码?

攻击者也不需要知道用户原始密码,攻击者只要拿到这个加密后的密码就能当作真正的密码来实现登录.


另外,对于防止脱裤后的破解者,靠谱的解决办法不是每个用户用不同的随机盐吗?
随机盐可以使得本来一个彩虹表就能解决的问题变成每个用户都要有一个彩虹表,直接造成彩虹表无效,破解难度爆表.
Y25tIGxpdmlk
35 天前
@iqoo #5 你的网站注册、登陆这类提交密码的操作并发很高吗??
gamexg
35 天前
@gamexg #48 不过考虑到现在基本都使用了 CDN,然后 CDN 处都有 https 证书,CDN 可以得到密码.
可能还真需要自己再套一层加密机制,或者登陆相关的域名不经过 CDN,
tool2dx
35 天前
@gamexg 是加盐啊,但是传统加随机盐,一个用户算起来还是太快了。

而 OP 的默认算法,一个用户在我机器上最起码要 3 秒才出结果,实在是够慢的。
dode
35 天前
正经的服务器都不是明文存储用户密码,比如谷歌。

你这每次认证算一个密码 hash 出来,提交到服务器,服务器没法知道你的密码对不对?
dode
35 天前
演示程序电脑上跑了 5 秒
DeWjjj
35 天前
后端存密码加的盐越多,密码就越安全,不过事实上没什么用。
现在基本靠二次验证做保护,账户内消费靠二级密码。
dode
35 天前
正经的安全是浏览器&密码管理器在每个网站密码框自动生成填充一个唯一密码
Chad0000
35 天前
@gamexg #54
其实没用,对于 CDN 来说,这个 Hash 过的密码也是密码,直接发给后端一样能登录。


@tool2dx #55
后端还可以将盐的算法跟用户名+版本号相关,这样有盐但不需要保存盐,只脱库没拿到应用的算法,破解者都不知道盐是什么(甚至盐会参与源密码进一步混淆),加大脱库难度。

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

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

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

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

© 2021 V2EX