Salt 是密码学中与密码、口令或其他输入一起参与 密码哈希密钥派生函数 的非秘密随机值,用来让相同输入在不同记录中得到不同输出,并抵抗预计算和彩虹表攻击。

核心问题

如果系统直接保存 hash(password),相同密码会产生相同哈希值。攻击者拿到数据库后,可以提前准备常见密码的哈希表,或者一眼看出哪些用户使用了同一个密码。

Salt 解决的是“让每条记录的哈希计算都变成独立实例”的问题。即使两个用户密码相同,只要 salt 不同,最终保存的派生结果也应该不同。

核心机制

密码存储中的典型形式是:

其中 password 是用户输入,salt 是每个用户或每条记录独立生成的随机值,cost 是计算成本参数。系统通常把 saltcost、算法版本和 stored 一起保存;验证登录时读取同一个 salt 和参数重新计算,再比较结果。

Salt 的关键要求是:

  • 唯一性:同一系统内不同密码记录应尽量使用不同 salt。
  • 随机性:用密码学安全随机数生成,避免可预测或集中碰撞。
  • 足够长度:常见做法至少 128 bit,降低碰撞概率。
  • 非秘密:salt 可以和哈希结果一起存储,它不是密钥。

Salt 的安全收益来自“让攻击者必须按每个 salt 单独计算”,而不是来自隐藏 salt。

工程用途

  • 密码存储:Argon2、bcrypt、scrypt、PBKDF2 等密码 KDF 都应配合 salt。
  • 密钥派生:从用户口令派生加密密钥时,用 salt 防止相同口令得到相同密钥。
  • 多租户隔离:不同用户、租户或文件使用不同 salt,减少批量攻击收益。
  • 算法迁移:保存 salt、成本参数和算法版本,方便未来升级哈希方案。

观察指标包括 salt 长度、重复率、随机数来源、算法版本覆盖率、成本参数分布和弱旧算法残留。

边界与常见坑

  • salt 不是密钥:泄露 salt 通常不是事故;泄露哈希数据库才是事故,salt 只是让破解成本更高。
  • salt 不替代慢哈希:只加 salt 但使用普通快速哈希,仍然容易被 GPU 批量暴力破解。
  • salt 不等于 Nonce:两者都可能要求不重复,但 salt 常用于密码哈希和 KDF 记录;nonce 常用于协议运行、加密消息或防重放。
  • 不要全局共用一个 salt:全局 salt 只能稍微阻碍预计算,不能让每条记录独立。
  • 不要把 salt 写死在代码里:写死会退化成全局常量,失去每条记录随机化的意义。
  • pepper 和 salt 不同:pepper 是服务端额外保存的秘密值,salt 是每条记录公开保存的非秘密值。
  • 比较结果要抗时序侧信道:验证密码时使用常量时间比较,避免从比较耗时泄露信息。

相关术语