Linux 上的密码哈希

最后更新: 2025-08-08 11:46:18 +0800 +0800
原文以英文撰写。您正在阅读AI翻译版本。

密码存储使用单向密码哈希函数,而非加密。验证程序使用存储值中嵌入的设置对提交的密码进行哈希,并比较编码后的结果。该设计不包含恢复明文密码的功能;攻击者取得数据库后,会改为离线测试候选密码。因此,现代密码哈希需要为每个密码生成随机盐值,并采用计算成本经过刻意提高且仍受支持的算法。

crypt 编码哈希 #

历史上的 crypt(3) 接口接受密码和设置。其模块化格式通常如下:

text
$id$parameters$salt$checksum

标识符和参数语法用于选择算法;盐值使相同密码产生不同的存储值;校验和则用于验证。验证时会将完整的存储字符串作为设置传入,因此应用程序必须原样保留该字符串。传统 DES crypt 改用双字符盐值,不适合生成新的密码哈希。

glibc 以前的 libcrypt 记录了 $1$(MD5)、$5$(SHA-256)和 $6$(SHA-512);SHA 格式接受 rounds=N。具备历史兼容性并不意味着 MD5 或 DES 适合新部署。可用方法和接受的标识符取决于具体实现和构建配置,而不是可移植的 POSIX 密码格式。

crypt 返回静态存储区,无法安全用于并发调用。crypt_r 使用调用方提供的 struct crypt_data;必须按照实现要求初始化该结构,且不得在线程间共享。比较前应检查调用是否失败:不同实现可能返回 NULL 或无效值。当应用程序自行处理哈希比较时,应采用常量时间比较,并在 API 允许后尽快清除密码材料。

c
struct crypt_data data = {0};
char *candidate = crypt_r(password, stored_hash, &data);
if (candidate != NULL && constant_time_equal(candidate, stored_hash)) {
    /* authenticated */
}

该示例仅说明验证机制,而非登录架构。支持 PAM 的程序应将身份验证委托给 PAM,而不应自行读取账户数据库,也不应自行实现提示和限流。

glibc 与 libxcrypt 的兼容性 #

crypt 是可选的 POSIX XSI 接口。glibc 2.39 移除了其源码树内置的 libcrypt;Linux 发行版通常改为提供 libxcrypt。libxcrypt 可以提供带有 glibc 兼容符号版本的 libcrypt.so.1,使链接到 glibc 历史库的现有 Linux 二进制文件仍可继续加载。未配置该兼容性的系统会安装不同的 ABI 名称,因此必须分别检查构建时和运行时兼容性。

除 crypt 和 crypt_r 外,libxcrypt 还提供源自 Openwall 的接口:

  • crypt_rn 接受调用方提供且明确指定大小的缓冲区;
  • crypt_ra 分配其工作存储区,或调整其大小;
  • crypt_gensalt、crypt_gensalt_rn 和 crypt_gensalt_ra 生成设置和盐值。

libxcrypt 支持一组可配置的历史及现代方案,包括在构建时启用的 yescrypt。不要仅根据标识符推断支持情况。应选择发行版支持的密码策略,使用操作系统的密码学随机源生成盐值,并采用相应方案文档规定的成本参数。提高成本会影响每一次交互式登录,因此必须进行容量测试;迁移期间必须保留对现有哈希的验证支持,直至每个账户在成功通过身份验证后完成重新哈希。

Shadow 文件的归属 #

/etc/passwd 必须保持可读,以便进程将数字 ID 映射到账户名。在采用 shadow 机制的系统上,其密码字段通常为 x,而密码哈希和有效期数据存放在 /etc/shadow 中。一条 shadow 记录包含账户名、编码哈希或锁定标记、上次更改日期、最短和最长有效期、警告期和非活动期、到期日期以及保留字段。

shadow 文件属于特权账户数据。其所有权和权限模式由发行版策略决定,但普通用户不得读取其中的哈希。shadow 工具套件提供 passwd、chpasswd、pwconv 和 pwunconv 等实用程序;代码不应直接解析并重写 /etc/shadow。NSS 查询 API 可以读取 shadow 数据,但访问控制仍然适用。

身份验证与迁移 #

登录时,支持 PAM 的服务会启动 PAM 事务。pam_unix 等模块通过应用程序的会话回调获取凭据,以所需权限读取已配置的账户数据,调用已配置的哈希实现,并向 PAM 栈报告成功或失败。账户有效期、锁定状态、多因素验证方法、目录身份验证和限流策略应由该栈及其支持服务负责。

迁移步骤如下:

  1. 在账户管理策略中配置受支持的当前算法及其成本;
  2. 仅在迁移期间启用旧版验证;
  3. 在下一次成功更改密码或验证登录时重新进行哈希;
  4. 对无法完成迁移的账户执行到期或重置操作;以及
  5. 当存储的哈希均不再需要旧版算法后,移除这些算法。

libxcrypt 接口文档、crypt(3) 和 shadow(5)介绍了平台特定的约定。

评论

在 GitHub 上阅读或评论(发表评论需要登录)