首页 存档 技术 查看内容

对抗拖库:Web前端慢加密

2018-3-30 13:00 |来自: 互联网 381 0

摘要: 21CTO社区导读: 近年来,太多的网站被拖库,包括CSDN,携程网,淘宝等公司。那么如何防止被拖库,可以看本篇文章找一下答案。 0x00 前言 天下武功,唯快不破。但密码加密不同。算法越快,越容易破。 0x01 暴力破 ...

21CTO社区导读:

近年来,太多的网站被拖库,包括CSDN,携程网,淘宝等公司。那么如何防止被拖库,可以看本篇文章找一下答案。

0x00 前言

天下武功,唯快不破。但密码加密不同。算法越快,越容易破。


0x01 暴力破解

密码破解,就是把加密后的密码还原成明文密码。似乎有不少方法,但最终都得走一条路:暴力穷举。

也许你会说还可以查表,瞬间就出结果。虽然查表不用穷举,但表的制造过程仍然需要。查表只是将穷举提前了而已。

密码加密,用的都是单向散列计算。既然单向,那就是不可逆,那只能穷举。

穷举的原理很简单。只要知道密文是用什么算法加密的,我们也用相同的算法,把常用的词组跑一遍。若有结果和密文一样,那就猜中了。

穷举的速度有多快?这和加密算法有关。加密一次有多快,猜一次也这么快。

例如 MD5 加密是非常快的。加密一次耗费 1 微秒,那破解时随便猜一个词组,也只需 1 微秒(假设机器性能一样,词组长度也差不多)。攻击者一秒钟就可以猜 100 万个,而且这还只是单线程的速度。

所以,加密算法越快,破解起来就越容易。

0x02 慢加密

如果能提高加密时间,显然也能增加破解时间。

如果加密一次提高到 10 毫秒,那么攻击者每秒只能猜 100 个,破解速度就慢了一万倍。

怎样才能让加密变慢?最简单的,就是对加密后的结果再加密,重复多次。

例如,原本 1 微秒的加密,重复一万次,就慢一万倍了:

for i = 0 ~ 10000
    x = md5(x)end

加密时多花一点时间,就可以换取攻击者大量的破解时间。

事实上,这样的「慢加密」算法早已存在,例如 bcryptPBKDF2 等等。它们都有一个难度系数因子,可以控制加密时间,想多慢就多慢。

加密越慢,破解时间越长。

0x03 慢加密应用

最需要慢加密的场合,就是网站数据库里的密码。

近几年,经常能听到网站被「拖库」的新闻。用户资料都是明文存储,泄露了也无法挽回。唯独密码,还可以和攻击者对抗一下。

然而不少网站,使用的都是快速加密算法,因此轻易就能破解出一堆弱口令账号。

当然,有时只想破解某个特定人物的账号。只要不是特别复杂的词汇,跑上几天,很可能就破出来。

但网站用了慢加密,结果可能就不一样了。如果把加密时间提高 100 倍,破解时间就得长达数月,变得难以接受。

即使数据泄露,也能保障「密码」这最后一道隐私。

0x04 慢加密缺点

不过,慢加密也有明显的缺点:消耗大量计算资源。

使用慢加密的网站,如果同时来了多个用户,服务器 CPU 可能就不够用了。要是遇到恶意用户,发起大量的登录请求,甚至造成资源被耗尽。

性能和安全总是难以兼得。所以,一般也不会使用太高的强度。

一些大型网站,甚至为此投入集群,用来处理大量的加密计算。但这需要不少的成本。

有没有什么方法,可以让我们使用算力强劲、同时又免费的计算资源?

0x05 前端加密

在过去,个人电脑和服务器的速度,还是有较大差距的。但如今,随着硬件发展进入瓶颈,这个差距正缩小。在单线任务处理上,甚至不相上下。

客户端拥有强大的算力,能不能分担一些服务器的工作?

尤其像「慢加密」这种算法开源、但计算沉重的任务,为何不交给客户端来完成?

过去,提交的是明文密码;现在,提交的则是明文密码的「慢加密结果」。无论是注册,还是登陆。

而服务端,无需任何改动。将收到的「慢加密结果」,当做原来的明文密码 就行。以前是怎么保存的,现在还是怎么保存。

这样就算被拖库,攻击者破解出来的也只是「慢加密结果」,还需再破解一次,才能还原出「明文密码」。

事实上,「慢加密结果」这个中间值,是不可能破解出来的!

因为它是一个散列值 毫无规律的随机串,例如 32 位十六进制字符串,而字典都是有意义的词组,几乎不可能跑到它!

除非字节逐个穷举。但这有 16^32 种组合,是个天文数字。

所以「慢加密结果」是无法通过数据库里泄露的密文「逆推」出来的。

或许你在想,即使不知道明文密码,也可以直接用「慢加密结果」来登录。事实上后端储存时再次加密,就无法逆推出这个散列值了。

当然,不能逆推,但可以顺推。把字典里的词组,用前后端的算法依次执行一次:

back_fast_hash( front_slow_hash(password) )

然后对比密文,即可判断有没有猜中。这样就可以用跑字典来破解。

但是有 front_slow_hash 这个障碍,破解速度就大幅降低了。

0x06 对抗预先计算

不过,前端的一切都是公开的。所以 front_slow_hash 的算法大家都知道。

攻击者可以用这套算法,把常用词组的「慢加密结果」提前算出来,制作成一个「新字典」。将来拖库后,就可以直接跑这个新字典了。

对抗这种方法,还得用经典的手段:加盐。最简单的,将用户名作为盐值:

front_slow_hash(password   username)

这样,即使相同的密码,对于不同的用户,「慢加密结果」也不一样了。

也许你会说,这个盐值不合理,因为用户名是公开的。攻击者可以对某个重要人物的账号,单独为他建立一个字典。

那么,是否可以提供一个隐蔽的盐值?答案是:不可以。

因为这是在前端。用户还没登录,那返回谁的盐值?登陆前就能获得账号的盐值,这不还是公开的吗。

所以,前端加密的盐值无法隐藏,只能公开。

当然,即使公开,单独提供一个盐值参数,也比用户名要好。因为用户名永远不变,而独立的盐值可以定期更换。


盐值可以由前端生成。例如注册时:

# 前端生成盐值salt = rand()password = front_slow_hash(password   salt)# 提交时带上盐值submit(..., password, salt)

后端将用户的盐值也储存起来。

登录时,输完用户名,就可以开始查询用户对应的盐值:

当然要注意的是,这个接口可以测试用户是否存在,所以得有一定的控制。

盐值的更换,也非常简单,甚至可以自动完成:

前端在加密当前密码时,同时开启一个新线程,计算新盐值和新密码。提交时,将它们全都带上。

如果「当前密码」验证成功,则用「新密码」和「新盐值」覆盖旧的。

这样更换盐值,还是只用到前端的算力。

这一切都是自动的,相当于 在用户无感知的情况下,定期帮他更换密码!

密文变了,针对「特定盐值」制作的字典,也就失效了。攻击者得重新制作一次。

0x07 强度策略

密码学上的问题到此结束,下面讨论实现上的问题。

现实中,用户的算力是不均衡的。有人用的是神级配置,也有的是古董机。这样,加密强度就很难设定。

如果古董机用户登录会卡上几十秒,那肯定是不行的。对于这种情况,只有以下选择:

  • 强度固定

  • 强度可变

1.强度固定

根据大众的配置,制定一个适中的强度,绝大多数用户都可接受。

但如果超过规定时间还没完成,就把算到一半的 Hash 和步数提交上来,剩余部分让服务器来完成。

[前端] 完成 70% ----
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

相关分类

返回顶部