# 数据加密 (/docs/configure/encryption)

Mix Space 提供了敏感配置的加密存储能力。默认为关。

为什么需要 Key 加密。

假设数据库被脱库。如果开启了 Key 加密，即便攻击者拿到了全部数据，也读不出被标记为加密的字段。同时，也需要谨慎开启此功能——开启后你需要记住加密密钥，否则你将永远丢失这些数据。

<Callout type="warn">
开启前请务必备份数据库。忘记密钥 = 这些数据永久无法恢复。
</Callout>

## 哪些字段会被加密

加密范围不是全部配置项，而是由服务端 schema 上的 `encrypt` 元数据决定，**随版本变化**。

判定规则：服务端遍历配置 schema 时，会读每个字段的 `encrypt` 元数据；**父级被标记时，整棵子树都继承加密**。绝大多数密码类字段是通过 `field.password()` / `field.passwordHalfGrid()` 定义的，这两个 helper 会在构造 schema 时自动注入 `encrypt: true`，所以你在配置里看到的「密码」输入框，基本都属于加密范围。

当前版本（v14.15.2）覆盖的主要字段：

| 区域 | 字段 |
| --- | --- |
| OAuth | 各服务商的 client secret、Apple 私钥 |
| 图床 / 对象存储 | S3、R2 的 `secretKey` |
| 邮件 | Resend API Key、SMTP 密码 |
| AI | 各 Provider 的 `apiKey` |
| Webhook | 签名密钥 |
| 搜索推送 | 百度推送 Token、Bing API key |
| 会员 | API Key |
| 其他 | 高德地图 Key、设备 Key、Personal Access Token 等 |

<Callout type="info">
想确认当前版本的准确清单，可以看服务端 `apps/core/src/modules/configs/` 下的 `configs.schema.ts`：凡是写成 `field.password(...)` 或 `field.passwordHalfGrid(...)` 的字段都会被加密，另加显式标注 `{ encrypt: true }` 的字段。文档这里的说明可能落后于代码，以源码为准。
</Callout>

## 如何开启

你可以附加 `--encrypt_enable` 来启动服务。如：

```bash
node out/main.mjs --encrypt_enable
```

<Callout type="warn">
**只要设置了加密密钥而没有显式关闭，加密就会被自动开启。**

判定逻辑是`ENCRYPT_ENABLE` 未设置时，回落到「是否设置了密钥」这一项。也就是说，你只填了密钥、没管开关，加密就已经生效了。如果暂时只想把密钥填进配置、暂不加密，必须显式写 `ENCRYPT_ENABLE=false`。
</Callout>

## 指定密钥

可以通过 `--encrypt_key <key>` 指定，也可以用环境变量。环境变量有两名，`MX_ENCRYPT_KEY` 优先于 `ENCRYPT_KEY`：

```bash
# 推荐
export MX_ENCRYPT_KEY=<64 位密钥>
# 等价写法
export ENCRYPT_KEY=<64 位密钥>
```

密钥的校验分两层，容易搞混：

| 情况 | 行为 |
|------|------|
| 提供的密钥**不是 64 位** | 启动即报错 |
| 提供的密钥**短于 64 位** | 不报错，服务端会用 `sha256(key + 'mx-encrypt-salt')` 派生并补齐到 64 位 |
| 提供的密钥**长于 64 位** | 启动即报错 |
| 提供的密钥**不是合法 hex 字符** | **启动时不校验**，直到真正加解密时才抛出 `Invalid key length` |

推荐用 `openssl rand -hex 32` 生成，产出的正是 64 位小写 hex，一次通过全部校验：

```bash
openssl rand -hex 32
```

加密算法可用 `ENCRYPT_ALGORITHM` 调整，默认 `aes-256-ecb`。

<Callout type="info">
  不指定密钥时，服务端默认取机器的
  [machine-id](https://www.npmjs.com/package/node-machine-id) 并派生。
</Callout>

<Callout type="warn">
  关闭加密开关**不会**把已经写入的密文还原成明文。此前加密过的字段会继续以密文形式存储和返回，密文带有 `$${mx}$$` 标识前缀。如果你打算关闭加密，需要先准备好原密钥完成解密。
</Callout>

## 疑难解答

如果出现 `Invalid key length`，说明你的密钥**不是合法的 hex 字符串**（长度问题在启动阶段就会被拦下，不会走到这里）。请用 `openssl rand -hex 32` 重新生成一个 64 位小写 hex 字符串。