网站其实不知道你的密码:它只存了一串"回不去的指纹"

网站其实不知道你的密码:它只存了一串

一、先分清:加密和哈希是两件事

加密哈希(如 bcrypt)
能否还原能,有钥匙就能解回去不能,单向函数
用途保护需要复原的数据验证"是否一致",不需要复原
存密码合不合适不合适:库一泄露,全部还原合适:泄露了也算不出原文

这就是为什么"存密码"必须用哈希。网站登录时的实际流程是:把你输入的密码再算一次哈希,与数据库里的值比对;一致就放行。它永远不需要知道原文。

二、盐:防止"撞表"的那把随机料

早期做法是直接哈希,问题很致命:同样的密码得到同样的结果。攻击者预先算好海量常见密码的哈希(彩虹表),一撞一个准;两个用户密码相同,哈希也相同,一眼就看出来。

bcrypt 的做法是在哈希前自动生成一个随机盐值,与密码一起参与运算。效果是:同一个密码,每次生成的哈希都不同;彩虹表直接失效,因为攻击者得为每个盐单独重算。

盐不需要保密,它和哈希一起存着。它的作用是让"预计算"这条路走不通,而不是让哈希变得更神秘。

三、成本因子:把暴力破解的时间成本抬高

bcrypt 的另一个关键参数是成本因子(work factor / rounds),常用默认与推荐值在 10 到 12 之间。它决定哈希要进行多少轮运算:

成本因子效果
偏低(如 10)计算快,登录响应快,但攻击者尝试速度也快
偏高(如 12)单次计算更慢,攻击者穷举的代价成倍上升;正常登录几乎无感

这个设计的巧妙处在于不对称:合法用户一天登录几次,慢几十毫秒无所谓;攻击者要试上亿次,慢一倍就是灾难。

四、看懂一行哈希:三段结构

标准 bcrypt 哈希长这样:$2b$12$m8sK9uXvZ9p8sK9uXvZ9pOhzZbD2n4sM6d7f8g9h0j1k2l3m4n5o6p

片段含义
$2b$算法标识(版本)
12成本因子,即轮数档位
后面那串盐值 + 哈希结果的组合

因为版本、成本因子、盐都写在同一行里,验证时不需要额外参数——这一点对系统迁移很重要:老数据能平滑校验,不必强制所有人重设密码。

五、验证流程与实操提醒

验证密码的正确姿势是调用库提供的比对函数(例如 bcrypt.compare),而不是自己拼接字符串再比。自己实现容易在编码、截断、比较方式上出错。

对普通用户的启示只有两条:一是别在多个网站用同一个密码,因为不同站点的哈希实现水平差异很大;二是看到"我们加密存储您的密码"这种说法,可以理解为"哈希",但如果一个网站能把你的密码原文发回给你,那才是真正该警惕的。