密码和加盐

引子

之前讨论过,表结构存储用户的账号密码的时候,可以用散列函数做密文处理,这其实可以扩展开,用户的账号密码存储是可以好好讨论的事情,可以根据业务的区别、用户量的大小等情况做不同的密码落库处理。

用户在设置密码时,不记录密码本身,只记录密码的散列值,只有用户自己知道密码的明文。校验密码时,只要输入的密码正确,得到的散列值一定是一样的,表示校验正确。

散列函数破解方法

通常情况下,当字段经过散列处理(如MD5),会生成一段散列值,而散列后的值一般是无法通过特定算法得到原始字段的。

但是某些情况,比如一个大型的彩虹表,通过在表中搜索该MD5值,很有可能在极短的时间内找到该散列值对应的真实字段内容。

字典法

提前构建一个“明文->密文”对应关系的一个大型数据库,破解时通过密文直接反查明文。但存储一个这样的数据库,空间成本是惊人的。

构建彩虹表(rainbow table)

在字典法的基础上改进,以时间换空间。是现在破解哈希常用的办法。

防止破解的方法

为了防止彩虹表破解,还可以为密码进行加盐处理,只要验证密码时,使用相同的盐即可完成校验。

盐(Salt),在密码学中,是指通过在密码任意固定位置插入特定的字符串,让散列后的结果和使用原始密码的散列结果不相符,这种过程称之为“加盐”。

加盐后的散列值,可以极大的降低由于用户数据被盗而带来的密码泄漏风险,即使通过彩虹表寻找到了散列后的数值所对应的原始内容,但是由于经过了加盐,插入的字符串扰乱了真正的密码,使得获得真实密码的概率大大降低。

加盐方法

方法1

整个项目都是唯一的一个不外泄漏的盐值

方法2

每个用户都是唯一的盐值,可以随机6位字符串,保存到sys_user表中,当做用户的一个字段落库。

方法3:

考虑这个盐不能进行存储,而是可以用现有的用户信息进行生成。可以用某种算法利用当前用户的信息(必须是固定不会修改的信息),比如用户id、用户注册时间等。这个算法也可以是哈希加密算法,比如将用户的几个信息进行一定的排序处理之后利用哈希生成盐。

最后,生成的密码方法是:

md5(md5(password),salt)

sha512(sha512(password),salt)
/**
 * 随机生成四位字符串的salt
 * 也可以根据实际情况使用6位或更长的salt
 */
function generateSalt()
{
    // 使用随机方式生成一个四位字符
    $chars = array_merge(range('A', 'Z'), range('a', 'z'), range('0', '9'));
    for ($i = 0; $i < 4; $i++) {
        $str .= $chars[mt_rand(0, count($chars) - 1)];
    }
    return $str;
}

/**
 * 密码生成
 * 使用两层hash,将salt加在第二层
 * sha1后再加salt然后再md5
 */
function generateHashPassword($password, $salt)
{
    return md5(sha1($password) . $salt);
}

使用取舍思考

密码本身是在安全等级与用户体验(密码复杂度X摘要成本) 的一个平衡。

很多to B的CRM并没有那么高的安全等级,(即破解成本«破解价值), 所以可以把用户体验做的更好些,比如密码简单一些,加盐逻辑简单些。

如果安全等级高,那密码本身的复杂度(长度要够,字符类型要多种)就要够,交叉验证(验证码、生物识别)等都要做上去。

唠嗑广场

一个观点,不一定对。

对于公司的盈利,分为共识和事实(不一定只有这两种)。

拿长江电力和贵州茅台为例子。

如果要重建一个长电,需要批复、移民、还有巨大的土木工程,这些都是必选项,无法绕开,都是需要投入的事实;

如果重建一个茅台(我不喝酒,可能屁股是歪的),需要时间、人力、不可替代的地理位置等等,但是绝对成本是不高的,茅台最大的价值是品牌,是在时间积累下的品牌溢价,大家觉得在招待的时候,拿出飞天茅台来就是对客人尊重,这是心理满足,所以茅台就变成了消耗品+精神食品。

如果共识被打破,那茅台的价值会大幅下滑。同时,共识被破坏的可能性比较低,建立共识也很难。

我们想象一下,如果哪一天,大家认为黄金没有收藏价值,无法兑现的场景,估计会很诧异。

PS: 作为一个太理性的人,就不喜欢黄金,因为觉得不能吃不能喝哈哈。