V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
zjcqoo
V2EX  ›  分享创造

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

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

    慢哈希就是计算速度很慢的哈希函数,例如 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   ·   实用小工具   ·   2627 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 15:41 · PVG 23:41 · LAX 07:41 · JFK 10:41
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.