自从出现了几次大的密码泄露事件后,密码安全也受到更多人的关注。密码管理的工具也层出不穷,有 LastPass,KeePass 等等的单独工具,也有浏览器带有的密码管理器。在这里,我推荐一种我自己现在使用的方法 - RandPsw,并分析不同方式的优缺点。

在这里只进行分析第三方加密工具的问题,由于用户自己泄露或者管理密码的弱口令等等的不做说明,因为这个都是密码管理工具做不到也是不应该做的。同样不做在网络传输过程中被劫持泄密,这个同样是设计缺陷。

先看看常用的几个密码管理工具

LastPass

LastPsss 是云端密码管理,有对应的客户端,兼容各大平台,易用性还是很好的。但是唯一的问题就是作为云端密码管理,云端被黑或者监守自盗都是风险,而且用户的密码都是可逆成明文的存储。所以出问题后危害是很大的。

KeePass

KeePass 使用本地数据库加密,除非知道管理密码,否则即使拿到数据库也无法解密,在安全性上没有问题,但是在多端使用或者使用别人的电脑等等问题时候就有些不太方便了,因为是本地数据库,所以不太容易做跨终端的访问;另一个问题就是数据库的丢失,数据库丢了就没办法找回密码。

浏览器自带

浏览器自带的密码管理器总体来说安全性还好,但是相较于 KeePass,如果电脑被黑密码就在劫难逃了,Chrome 早些版本就是密码直接可以明文访问,后来加了计算机密码验证,但是安全性也不高;同样还是存在 KeePass 的问题,在不同终端上面或者 App 上面就有些不方便了,Chrome 的密码在用户登录情况下会做云端同步,但是云端同步的话又有了 LastPass 的问题。

综合上面,基本上密码管理都会做自己的一份密码库,密码库要么放在云端,要么在本地,因为是密码管理,所以必然可以从库中恢复真正的密码,所以库的安全性就很重要了,密码库放在云端,可是对云端的管理者不放心,放在自己本地又怕丢和多终端上不太好操作,那有没有不同库的方法呢?下面就介绍我现在使用的方法。

推荐 RandPsw

先称我现在使用的这个方法为 RandPsw,就是 rand password 的意思,这个是在 阿肆 之前的基础上更改而成的,基本的原理就是根据页面输入的密码,当前的域名再加盐加密,根据加密出来的结果依照位加密前密码的格式生成新的密码,是一个对密码进行加密的服务吧,而不是一个密码管理的服务。在这个过程中没有使用数据库,所以也就不存在数据的保险问题;没有进行网络连接,完全是本地加密,所以可以放心使用;加密算法是不可逆而且是公开的,所以在不同的终端上面实现起来也不会有太大的问题。

说了这么多优点,缺点呢?就是操作上可能会复杂一些;每次都要根据域名,输入的密码,Salt 加密;所以比较繁琐,不优雅。

根据这个思路,还是在阿肆的基础上完善了 Chrome 的插件,在初始使用的时候需要在插件选项页输入 Salt,并加密保存,这个会跟随 Chrome 的账号做同步;
然后就是在每次登录的时候,输入正常的密码,双击密码框,看到浏览器地址栏右侧出现了一个绿色的小锁子,就表示加密成功。在这之前,肯定要去更改你的密码;在插件中,网站域名是脚本去取的,所以不用关心。原则上是同样的网站域名+同样的加密前密码+同样的Salt = 同样的加密后密码,基本上不同的网站使用同一份加密前密码也会生成不同的加密后密码。这样,记住一份密码,自己的盐,就可以知道自己注册过网站的密码了。

这样就有了一个问题,多个网站域名共用同一套密码,例如 tmall.com 就是使用 taobao.com 的账号登录的,这样提供了 Domain Map 的功能,可以将当前登录的页面映射到之前账号的域名,这里既将 tmall.com 映射到 taobao.com,如下图。

域名映射

还有问题就是,各个网银,支付宝都会有自己的安全插件,这样子插件就不能正常工作了,所以在插件选项页提供手动生成密码的功能,输入网站当前页地址,加密前密码,就会自动调用 Domain Map 和 Salt 来计算,生成的密码默认是不显示的,可以点击复制到剪贴板,这样就不会暴漏密码出来,如下图。

密码手动加密

还有就是在不同的终端上面使用了,现在因为我自己是做前端开发的,所以只做出了 Web 版的,在不同的设备上面访问 http://fetools.duapp.com/randpsw.html 这个地址就是托管在 GitHub 上面的 Web 版,虽说是 Web 版,但是加密都是进行本地加密,没有将密码做网络传输等,所以在移动设备或者其他人电脑上面就比较麻烦些;要自己访问 Web 做加密。

最后说说本身的加密,这里的加密基本上都是使用 MD5 加盐来做的,所以安全性还是比较有保证的,从加密结果生成原密码格式的密码就是两个原则,一是加密后密码长度和原密码长度一样长,二是将原密码中的大小写字母,数字,特殊字符替换成同样的类型,并做重新排序,所以原密码中不出现特殊字符,加密后的密码也不会出现,正好对应了某些网站的特殊要求(例如某火车票官方订票网站,就不允许密码中出现除过下划线外的特殊字符)。下面给出密码加密的算法的 JavaScript 版本,同时在文末给出 GitHub 的代码地址,有兴趣的童鞋也可以自己去实现。

function randpsw(value, domain, salt) {
    salt = salt || '';
    value = value.toString();
    domain = domain.toLowerCase();

    var hash = new BigInteger('0x' + md5(value + domain + salt)),
        pswArr = value.match(/./g) || [],
        i = 0, length = pswArr.length,
        randpswArr = new Array(length),

        // 替换密码算法
        replacePswChar = function (index, type) {
            var charList = [
                'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
                '0123456789',
                '~!@#$%^&*'

            ],
            length = charList[type].length,
            aryNum = hash.toString(length);

            index = index % aryNum.length;
            return charList[type].charAt(parseInt(aryNum.charAt(index), length));
        },

        // 打乱密码算法
        randArr = function (arr) {
            var len = arr.length,
                i, j,
                tmp, md5r,
                key1, key2;
            for (i = 0; i <= len; i++) {
                for (j = 0; j <= len - 1; j++) {
                    md5r = md5(arr[j] + arr[j + 1]);
                    key1 = parseInt(md5r.substr(0, 4), 16) % len;
                    key2 = parseInt(md5r.substr(4, 8), 16) % len;
                    if (key1 === key2) {
                        continue;
                    }
                    tmp = arr[key1];
                    arr[key1] = arr[key2];
                    arr[key2] = tmp;
                }
            }
            return arr;
        },
        single;

    // 循环替换
    for (i = 0; i < length; i++) {
        if (pswArr[i].search(/[A-Z]/) === 0) {
            single = replacePswChar(i, 0).toUpperCase();
        } else if (pswArr[i].search(/[a-z]/) === 0) {
            single = replacePswChar(i, 0).toLowerCase();
        } else if (pswArr[i].search(/[0-9]/) === 0) {
            single = replacePswChar(i, 1);
        } else {
            single = replacePswChar(i, 2);
        }

        randpswArr[i] = single;
    }

    // 打乱
    return randArr(randpswArr).join('');
}

相关链接

Web 地址提供个二维码,供移动设备扫描使用

Web 版地址二维码

参考资料