> For the complete documentation index, see [llms.txt](https://book.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://book.bsdcn.org/di-27-zhang-an-quan/di-27.2-jie-zhang-hu-ren-zheng-an-quan.md).

# 27.2 账户认证安全

FreeBSD 通过 **/etc/master.passwd** 主密码文件与 `pam_unix.so` 认证模块实现账户安全管理。本节介绍密码数据库生成流程、账户锁定与 nologin 禁止登录、SHA-512 哈希验证及 pam\_passwdqc 密码策略配置。

## 密码文件目录结构

**/etc/master.passwd** 是主密码文件，仅 root 可读，内含用户加密后的密码。

**/etc/passwd** 仅为兼容而保留，由 pwd\_mkdb(8) 自动生成，密码字段替换为 `*`。

```sh
/etc/master.passwd
        │
        │  pwd_mkdb(8)
        ▼
────────────────────────────
      数据库生成过程
────────────────────────────
        │
        ├──────────────┬──────────────┐
        ▼              ▼              ▼
/etc/pwd.db     /etc/spwd.db     /etc/passwd
（db(3)格式）    （db(3)格式）     （ASCII 密码兼容文件）
（无密码）        （含密码）        （无密码）
```

为保证数据一致性，**所有密码文件均不应手动编辑**，应使用 vipw(8)、chpass(1) 或 pw(8) 等工具修改。

## 阻止登录

保护系统的首要措施是审计账户，禁用所有不需登录的账户。

> **思考题**
>
> > 确保 `root` 拥有强密码，定期轮替，且不应共享该密码。
>
> 此说法较为流行，实则存在安全隐患：
>
> * 强密码复杂难记，致使用户依赖不安全的存储方式（如纸质记录或文本文件），反致安全风险增加。
> * 定期更换密码加剧管理难度，而“密码管理工具”本身的信任问题亦难以回避。
> * 基于密钥的认证方式虽提供了一定安全保障，但密钥一旦丢失，身份验证将更加复杂。
> * 多因素认证（MFA）虽能增强安全性，但其依赖的设备一旦遗失或程序被卸载，将引发同类问题。

拒绝账户登录有两种方式，具体如下。

其一，锁定账户。下面演示如何锁定账户 `ykla`：

```sh
# pw lock ykla
```

锁定后，用户 `ykla` 尝试登录：

```sh
FreeBSD/amd64 (ykla) (ttyv1)

login: ykla
Password:
Login incorrect
login:
```

其他用户尝试切换至 `ykla`：

```sh
# su ykla
su: Sorry
```

> **警告**
>
> 切勿尝试锁定 root 账户。
>
> ```sh
> ykla@ykla:~ $ su
> Password:
> su: Sorry
> ```
>
> 否则可能需要进入单用户模式才能恢复。

解锁用户 `ykla`：

```sh
# pw unlock ykla
```

其二，将 shell 设为 **/usr/sbin/nologin**，以此阻止登录。用户尝试登录时，nologin shell 阻止系统分配 shell。

仅 root 有权更改其他用户的 shell：

```sh
# chsh -s /usr/sbin/nologin ykla
```

用户 `ykla` 登录被阻止，效果与前例类似。

要恢复登录，可重新为用户 `ykla` 分配 sh：

```sh
# chsh -s sh ykla
```

> **警告**
>
> 切勿尝试为 root 账户分配 **/usr/sbin/nologin**。

## 密码哈希

既然必须使用密码，密码就应当足够复杂，并以强哈希机制加密后存入密码数据库。FreeBSD 的 `crypt()` 库支持 SHA-256、SHA-512、Blowfish 等多种算法。

不应将默认的 SHA-512 降级为更弱的哈希算法，也不建议替换为 Blowfish。Blowfish 算法不属于 AES 家族，也不符合联邦信息处理标准（FIPS）要求，部分环境可能无法通过合规审查。

```sh
密码哈希验证流程（以 SHA-512 为例）：

  用户输入明文密码
        │
        ▼
  crypt() 库接收密码
        │
        ├── 从 /etc/master.passwd 读取存储的哈希值（$6$盐$哈希）
        │
        ▼
  提取盐值（Salt），使用 SHA-512 对 "明文密码 + 盐值" 重新计算哈希
        │
        ▼
  比较新哈希与存储哈希
        │
        ├── 匹配 ──► 认证通过，分配 Shell
        │
        └── 不匹配 ──► 认证拒绝，提示 "Login incorrect"
```

### 查看当前用户密码使用的哈希

如果要确认用户所用哈希算法，root 用户可查看 FreeBSD 密码数据库中对应哈希值。每个哈希值均以一个符号开头，表明加密该密码的哈希机制类型：

```sh
$编号$盐$哈希
```

| 哈希算法    | 前缀标识                  |
| ------- | --------------------- |
| DES     | 无编号                   |
| MD5     | `$1$`                 |
| bcrypt  | `$2a` / `$2b` / `$2y` |
| NT-Hash | `$3$`                 |
| SHA-256 | `$5$`                 |
| SHA-512 | `$6$`                 |

编号 4 未使用。

本例中，`ykla` 的密码以 `$6$` 开头，即采用默认的 SHA-512 算法。

```sh
# grep ykla /etc/master.passwd
```

注意，密码数据库中存放的是加密后的哈希值，而非原始密码。输出应类似于以下内容：

```sh
ykla:$6$SqMJXrv5aC6Wq.by$nmbZs078aHNBVyh9noLFouJsGHyFSvQIzH0W4zpdfXuPtGtt.FHgWfXDHVBa.g9P0eZ32UwfByzRKdVnTaO7W.:1001:1001::0:0:User &:/home/ykla:/bin/sh
```

### 在登录类别中设置默认密码哈希

`passwd_format` 默认值为 **sha512**。有效值包括 `blf`、`sha256`、`sha512`（推荐）以及 `des`、`md5`、`nth`（仅用于兼容旧密码文件，不应主动选用，参见 crypt(3)）。

运行以下命令可检查当前所用哈希机制：

```sh
# grep passwd_format /etc/login.conf
```

输出应类似于以下内容：

```ini
	:passwd_format=sha512:\
#	:passwd_format=des:\ # 这是注释行，可忽略
```

例如，要切换为 Blowfish，可将该行修改为：

```ini
:passwd_format=blf:\
```

接着，必须执行 `cap_mkdb` 来更新 login.conf 数据库：

```sh
# cap_mkdb /etc/login.conf
```

注意，此更改不会影响已有的密码哈希。也就是说，所有用户都需要运行 `passwd` 修改一次密码，才能让新算法生效。

### 密码策略执行

为本地账户实施强密码策略，属于系统安全基本配置。FreeBSD 以内置的可插拔认证模块（PAM）控制密码长度、强度与复杂度。

本节使用 `pam_passwdqc` 模块（用户修改密码时生效）设置密码最短与最长长度，并强制混合字符。

> **思考题**
>
> 强制要求密码混合字符，密码轮替、禁用常见词汇等复杂性规则，已不再视作最佳安全实践。
>
> 分析这两种安全哲学。

配置时先切换至 root 用户，随后取消 **/etc/pam.d/passwd** 文件中 `pam_passwdqc.so` 所在行的注释。

```sh
#
#
# PAM configuration for the "passwd" service
#

# passwd(1) does not use the auth, account or session services.

# password
#password       requisite       pam_passwdqc.so         enforce=users # 移除行首的 # 以启用此模块
password        required        pam_unix.so             no_warn try_first_pass nullok
```

随后依策略编辑该行：禁用第一至第三类密码（含密码短语），仅允许四类字符密码（≥10 位）或三类字符密码（≥12 位）；新旧密码不得相似；失败最多重试 3 次；仅对普通用户强制执行（root 不受约束）。

```ini
password        requisite       pam_passwdqc.so         min=disabled,disabled,disabled,12,10 similar=deny retry=3 enforce=users
```

参数含义：

`min=disabled,disabled,disabled,12,10` 对应五种密码复杂度：

| 序号 | 字符类要求    | 最小长度 | 状态 |
| -- | -------- | ---- | -- |
| 1  | 仅一种字符类   | —    | 禁用 |
| 2  | 仅两种字符类   | —    | 禁用 |
| 3  | **密码短语** | —    | 禁用 |
| 4  | 包含三种字符类  | 12 位 | 启用 |
| 5  | 包含四种字符类  | 10 位 | 启用 |

字符类包括：数字、小写字母、大写字母和其他字符。非 ASCII 字符如果无法归类，视作非数字类。

保存文件后，用户修改密码时会看到类似下面的提示：

```sh
$ passwd
```

输出应类似于以下内容：

```sh
Changing local password for ykla
Old Password:

You can now choose the new password.
A valid password should be a mix of upper and lower case letters,
digits and other characters.  You can use a 12 character long
password with characters from at least 3 of these 4 classes, or
a 10 character long password containing characters from all the
classes.  Characters that form a common pattern are discarded by
the check.
Alternatively, if noone else can see your terminal now, you can
pick this as your password: "embark,France_list".
```

密码如果不符合策略，系统拒绝并给出警告，例如：

```sh
Weak password: too short.
```

用户可在配置的重试次数内多次尝试。

如果组织要求密码定期过期，FreeBSD 可在 **/etc/login.conf** 的用户登录类别中设置 `passwordtime` 参数。

`default` 登录类别中包含一个示例：

```ini
#       :passwordtime=90d:\
```

要为此登录类别设定 90 天过期，移除注释符（`#`），保存后执行：

```sh
# cap_mkdb /etc/login.conf
```

如果要为单个用户 `ykla` 设置密码更改日期（password aging，密码必须在指定日期之前更改），可向 `pw` 同时传递日期（或距离过期的天数）和用户名：

```sh
# pw usermod -p 31-05-2026 -n ykla
```

日期以日、月、年的格式设置（`dd-mmm-yy[yy]`，其中月份可为数字或字母缩写）。

如需取消密码更改日期限制，将其设为 0 即可。

```sh
# pw usermod -p 0 -n ykla
```

> **注意**
>
> `-p` 设置的是密码更改日期（password aging），与账户过期是不同概念。如需设置账户过期时间（account expiry），应使用 `-e` 参数。

从今天起，指定用户 ykla 在 30 天后账户过期：

```sh
# pw usermod ykla -e +30d
```

从今天起，指定用户 ykla 在一年后过期：

```sh
# pw usermod ykla -e +1y
```

## 课后习题

1. 在 FreeBSD 中，**/etc/master.passwd** 文件中存储的密码哈希值以 `$6$` 前缀开头，查阅资料说明该前缀对应的哈希算法，并与 `$2b$` 前缀进行安全性对比。
2. 使用 `pw lock` 命令锁定一个测试账户后，分别通过本地终端与 SSH 尝试登录，记录两次尝试的错误信息差异。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://book.bsdcn.org/di-27-zhang-an-quan/di-27.2-jie-zhang-hu-ren-zheng-an-quan.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
