用 asm.js 写了一个慢哈希的 demo - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
zjcqoo
V2EX    分享创造

用 asm.js 写了一个慢哈希的 demo

  •  
  •   zjcqoo 2015-08-13 14:09:47 +08:00 5652 次点击
    这是一个创建于 3799 天前的主题,其中的信息可能已经有所发展或是发生改变。

    慢哈希就是计算速度很慢的哈希函数,例如 bcrypt,计算一次花费几百毫秒的时间,这样就大幅降低破解的速度。

    但这样会给服务器很大的计算压力,容易被拒绝服务。

    不过现在浏览器的性能越来越好了,而且支持多线程计算,甚至还出现了 asm.js 这样的高性能方案,所以可以把慢函数的计算放到前端来完成。

    注册时,用户可以选择一个合适的时间强度,浏览器在该时间段内,尽可能多的重复计算 Hash 函数,最后将 账号、Salt(前端生成)、Hash、重复次数 提交。

    图1

    登陆时,先获取指定用户的 Salt 和 次数,然后将 Hash(密码 + Salt) 重复 turn 次,消耗注册时指定的时间,最后提交。

    图2

    这样就算被拖库,破解成本也大幅增加。相比传统的简单 Hash,要慢上很多个数量级。

    而且在前端计算还有另外一个好处:可以增加登录成本。对于每个用户来说,登录时多等一两秒并没多大影响,无非就是输验证码的时间。但对于撞库的人来说,就是很大的瓶颈了。

    Demo: http://121.43.101.95:8080/

    30 条回复    2015-08-17 05:23:24 +08:00
    Numbcoder
        1
    Numbcoder  
       2015-08-13 14:26:43 +08:00
    这样的话,所有人的 salt 都被暴露了把
    zjcqoo
        2
    zjcqoo  
    OP
       2015-08-13 14:29:38 +08:00
    这个 salt 给前端计算用的。后端储存的时候可以和传统一样,用隐藏的算法和 salt。
    feiyuanqiu
        3
    feiyuanqiu  
       2015-08-13 14:30:24 +08:00
    有个疑问,我看登陆的时候你是直接在前端计算出hash后的密码,这个密码和数据库保存的是一样的
    那么如果这个请求被嗅探了密码不就泄露了吗
    w88975
        4
    w88975  
       2015-08-13 14:30:59 +08:00
    慢hash是什么玩意?
    Numbcoder
        5
    Numbcoder  
       2015-08-13 14:32:16 +08:00
    @feiyuanqiu 你直接用户名密码登录难道就不会被嗅探?
    feiyuanqiu
        6
    feiyuanqiu  
       2015-08-13 14:32:39 +08:00
    @feiyuanqiu 你现在的方案其实就是明文保存密码吧
    tabris17
        7
    tabris17  
       2015-08-13 14:33:02 +08:00
    你这最多防网络嗅探而已,不防爆库
    Numbcoder
        8
    Numbcoder  
       2015-08-13 14:33:57 +08:00
    @zjcqoo 其实服务端直接用 bcrypt 并不会慢多少,Rails 早就默认用 bcrypt 了
    feiyuanqiu
        9
    feiyuanqiu  
       2015-08-13 14:34:42 +08:00
    @Numbcoder

    关键是他现在的方案其实就是明文保存
    这是登陆请求: http://121.43.101.95:8080/ajax_login?user=123&pwd=73590a3516e926c0e6677943ac2abcd3

    这是后台保存的密码:
    用户名 MD5(密码 + Salt) * N Salt N (百万) 最后登录时间 最后登录地址 登录成功 登录失败
    123 73590a3516e926c0e6677943ac2abcd3 ncnxirug 14 2015-08-13 14:27:31 182.150.24.209 5 0
    zjcqoo
        10
    zjcqoo  
    OP
       2015-08-13 14:40:12 +08:00
    @feiyuanqiu 拖库的话,直接提交 Hash 是可以登上其他人的账号的。这个是减慢破解某个账号的原密码。
    Numbcoder
        11
    Numbcoder  
       2015-08-13 14:41:44 +08:00
    @feiyuanqiu 不是明文的
    password --> bcrypt + salt --> md5 + salt


    但实际上 bcrypt 这一步根本就没用。如果被暴库,只需要破解 md5 这一步得到 bcrypt 的 hash,然而有了这个 hash 就可以直接登录了。。。
    feiyuanqiu
        12
    feiyuanqiu  
       2015-08-13 14:45:37 +08:00
    @zjcqoo 在这个方案里,原密码其实变成了在用户浏览器哈希后的那个密码,因为传给服务器的就是这个,用户最开始设置的是什么已经无关紧要了。而这个密码现在是以明文保存的,只要脱了库就全完了,连破解都不需要
    而如果要在服务器再进行一次加盐哈希的话,那么在前端的哈希就完全没有意义了
    zjcqoo
        13
    zjcqoo  
    OP
       2015-08-13 14:54:34 +08:00
    @feiyuanqiu 但还是难破解原密码。小网站的账号被控制就算了,但原密码被破解,就可以拿去猜其他网站同密码的账号了。
    yyfearth
        14
    yyfearth  
       2015-08-13 15:04:51 +08:00   1
    @zjcqoo @Numbcoder @feiyuanqiu

    其实这个方案我很久以前就考虑过
    LZ的方案还不够完善 导致前端hash变得几乎没有意义

    不过前端hash还是有办法弄得更加安全的
    我之前想出来的办法就是多次分开加密

    注册时候
    password --> browser: bcrypt + client salt (2000x) --> server:bcrypt + client salt + server salt (100x) -> db

    验证的时候
    password --> browser: bcrypt + client salt (random 1k+ times) --> server: bcrypt + client salt (2000-client random times) --> server:bcrypt + client salt + server salt (100x) == db

    这样就避免了“前端的哈希就完全没有意义了”的问题
    然后 由于用同样算法 只是循环次数不同
    分一大部分浏览器做 另一小部分服务器做 只要总数一定 结果就应该一样
    然后再换一个算法另外一个salt(可以是固定的)保存到数据库 来避免直接脱库的问题
    在注册的时候 浏览器做了最大次数的加密
    在验证的时候 由于浏览器是随机选择次数 所以可以做到每次结果不一样 避免了和注册时候传输同样的密文
    feiyuanqiu
        15
    feiyuanqiu  
       2015-08-13 15:24:42 +08:00
    @yyfearth 你这个思路不错呀,我去试试

    我记得腾讯微博的登陆也是在浏览器进行了处理再传送的,但是我一直还没有去看它是怎么处理的
    Numbcoder
        16
    Numbcoder  
       2015-08-13 15:40:37 +08:00
    @yyfearth 实在不明白为什么要搞得这么复杂

    https + 服务端 bcrypt(password + salt) 就能搞定的事需要这么麻烦吗?
    bumz
        17
    bumz  
       2015-08-13 16:48:05 +08:00
    其,有什卵用。於使用重密的人,出木桶效;不使用重密的人,自然也是用的。
    RIcter
        18
    RIcter  
       2015-08-13 16:59:00 +08:00
    lz 现在跳阿里了吗?是做前端还是做安全的_(:3」∠)_
    zjcqoo
        19
    zjcqoo  
    OP
       2015-08-13 17:16:28 +08:00
    @RIcter 前端安全:)
    iyangyuan
        20
    iyangyuan  
       2015-08-13 19:18:37 +08:00 via iPhone
    在浏览器端做这些只是自欺欺人而已,让https情何以堪
    yyfearth
        21
    yyfearth  
       2015-08-14 02:15:46 +08:00
    @Numbcoder @iyangyuan

    我之前也是这么想的 所以没有去实现 可是https已经颜面扫地了
    现在看来 https 只是最低保障 不用https是肯定不行的
    但是用https就没问题么?heartbleed 不就可以泄漏用户原密码么
    所以前端做预处理 还是有点意义的 可以保护用户的原始密码
    至少可以让弱密码看上去也不那么弱 就算是弱密码也得算个半天
    rtyurtyu
        22
    rtyurtyu  
       2015-08-14 09:44:18 +08:00
    自欺欺人,没有任何意义的方法,纯粹浪费地球资源
    前面已经有人总结的很好了

    说个其他方面的副作用:
    所有记住密码的功能、扩展、插件等等都会失效
    妨碍了用户的正常使用功能
    Numbcoder
        23
    Numbcoder  
       2015-08-14 10:21:56 +08:00
    既然采用了 https,就应该默认他是安全的,就好比你用 bycrpt, rsa,你怎么知道这些就是安全呢?
    如果按照你这逻辑扣下去,linux,mysql 都可能存在未知的漏洞和 bug,就都不能用了?这世界上也没有一个软件或系统是绝对安全的。

    再说了 heartbleed 是 openSSL 的问题,和 https 有毛关系啊。
    Numbcoder
        24
    Numbcoder  
       2015-08-14 10:22:13 +08:00
    @yyfearth 忘了@你
    yyfearth
        25
    yyfearth  
       2015-08-14 11:02:13 +08:00
    @Numbcoder 就算可以认为https协议本身是安全的 但是不能说明他的实现都是安全的
    heartbleed 就是一个例子 而且openSSL也是适用最广泛的实现 而且之后还出现了很多很多漏洞
    足以说明仅仅依靠https传输明文密码已经没有想象中那么安全可靠

    而且我是说 “https一定要用” 而不是“有漏洞就不能用”
    既然一套系统不够安全可靠 那么可以依赖多套系统叠加增加破解的难度
    就算采用了 https 但是也不能默认他是安全的 因为硬件升级和算法的更新
    很多https支持的老算法和设置也逐渐变的不再安全 比如 MD5 RC4 SHA1 SSL3 这样的算法或版本
    你需要做一些额外的设置和机制 才可以跟上安全的步伐

    更况切 对于 “https + 服务端 bcrypt(password + salt)” 就足够 我并没有反对
    LZ这个帖子主要是为了解决后端慢哈希函数bcrypt效率低下 可能有DoS的风险
    所以把一部分计算量放到前端来实现的想法 这个和用https并不冲突
    只是LZ的想法还不够完善 所以我把我之前的想法发出来供LZ和大家讨论
    我的想法的核心思想是 必须拥有原始密码才可以通过验证 而且把尽量多的运算量移到前端
    后端只需要一部分的计算量就可以达到 原本需要很大计算量的效果
    yyfearth
        26
    yyfearth  
       2015-08-14 11:07:53 +08:00
    @rtyurtyu 我之前也是这么认为的 但是现在觉得是一个选择 如果对安全的需求比较高的话

    对于 “说个其他方面的副作用: 所有记住密码的功能、扩展、插件等等都会失效 妨碍了用户的正常使用功能”
    这个问题并不大 目前很多webapp已经都是使用 Ajax 来提交用户信息 而不是直接通过Form来提交
    所以不会导致修改Form引起插件、扩展抓到hash后的密码
    目前国内有些网站是这样做的 我也觉得不太好

    对于ajax提交而不是form提交 一些插件、扩展抓取不到
    是这些都是他们自己的问题 因为我用的1password以及Chrome/Safari都没问题 可以抓取
    说明其他的插件、扩展也可以做到
    rtyurtyu
        27
    rtyurtyu  
       2015-08-14 11:53:36 +08:00
    @yyfearth ff自己的记住密码功能就记不住ajax提交,它只在form提交执行时才能抓取
    ajax千变万化的,我觉得这个不能赖ff,应该是网站开发者去想办法适配浏览器

    另外你上面的改进想法,其实本质上跟lz的一样
    每次随机n次传不一样的hash,这并没有增强安全性,因为抓包的抓到任何一个hash都能当成正确密码来用
    而且你这样还得带着n给服务器端
    这个只迷惑了自己,而不是别人
    NeoAtlantis
        28
    NeoAtlantis  
       2015-08-14 21:20:09 +08:00 via Android
    在浏览器上搞密码一般是个坏主意,stackoverflow上对此多次警告,理由和楼上各位差不多。不要自己发明轮子,用了https没必要加密,不用https那么js自己就不安全。

    我个人觉得有https再加密也是可以的,有时候设计好了可以做到mega网盘那种让服务器也不知道你的内容的安全。但是bcrypt这样的算法只在其特别有效率地运行时才有意义,所以js上来搞有点难。
    NeoAtlantis
        29
    NeoAtlantis  
       2015-08-14 21:21:36 +08:00 via Android
    不要自作聪明设计密码算法!
    不要自作聪明设计密码算法!
    不要自作聪明设计密码算法!

    重说三
    notcome
        30
    notcome  
       2015-08-17 05:23:24 +08:00 via iPhone
    如果中间人攻击的话,似乎怎么整都没有用,所以我之前一个小网战直接 HTTPS 明文密码,到服务器端 bcrypt。不过现在感觉至少也要 hash 一下。

    不过那个网站是真的超小超 ad hoc。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     4487 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 01:05 PVG 09:05 LAX 17:05 JFK 20:05
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86