/api/login/sync 的时序攻击严重性: 高 (CVSS 7.4)
受影响软件: TriliumNext/Trilium < 0.101.0
漏洞类型: CWE-208 – 可观察的时序差异
修复版本: Trilium 0.101.0 (PR #8129)
发布日期: 2026-02-06 | 保留日期: 2025-12-19
Trilium Notes 是一款开源、跨平台、层次化的笔记应用程序,专为构建大型个人知识库而设计。它支持:
同步功能允许 Trilium 客户端向 Trilium 服务器进行身份验证,以便笔记在设备间保持同步。该同步端点正是 CVE-2025-68621 的入口点。
时序攻击是一种旁路攻击,攻击者通过测量系统处理不同输入所需的时间来获取秘密信息。
经典例子是字符串比较:
"correct_password" !== "aorrect_password" → 在第 0 位失败 → 快速
"correct_password" !== "cXrrect_password" → 在第 1 位失败 → 稍慢
"correct_password" !== "correct_password" → 完全匹配 → 最慢
大多数编程语言在比较字符串时逐字符进行,并且一旦发现不匹配就立即停止(提前退出)。这意味着:
修复方法是使用常量时间比较函数,该函数无论在哪里出现不匹配,始终检查每个字节。
该漏洞通过对 Trilium 认证逻辑的手动代码审查发现。研究人员检查了 apps/server/src/routes/api/login.ts 中的同步登录流程,并在 loginSync() 函数(约第 111 行)中注意到以下模式:
const documentSecret = options.getOption("documentSecret");
const expectedHash = utils.hmac(documentSecret, timestampStr);
const givenHash = req.body.hash;
if (expectedHash !== givenHash) { // ← 有漏洞的行
return [400, { message: "Sync login credentials are incorrect..." }];
}
危险信号是使用 JavaScript 内置的 !== 运算符比较 HMAC 哈希。!== 运算符不是常量时间——一旦发现不同字符就退出。由于比较是在纯字符串上进行的(未使用密码学安全的比较函数),响应时间会泄漏攻击者猜测中有多少个前导字节是正确的。
研究人员随后问道:
“这种微小的时序差异在网络中能否被放大到足以恢复完整的 44 字符 Base64 编码 HMAC 哈希?”
答案是可以——通过足够多的重复测量和统计数据分析,信号可以超越噪声。
当 Trilium 客户端想要同步时,它会使用 JSON 主体调用 POST /api/login/sync:
{
"timestamp": "2025-12-19T10:00:00.000Z",
"syncVersion": 34,
"hash": "<HMAC-SHA256 of documentSecret + timestamp, Base64-encoded>"
}
逐字节恢复的工作原理如下:
对于位置 = 0 到 43:
对于字符集(A-Z,a-z,0-9,+,/,=)中的每个候选字符 c:
发送 SAMPLES 个请求,hash = known_prefix + c + 填充
记录平均响应时间
最佳字符 = 平均时间最高的候选字符
将该字符追加到 known_prefix
经过 44 次迭代(每个 Base64 字符一次),完整的 44 字符 HMAC 哈希被恢复。
实际需求:
time.perf_counter() 提供纳秒分辨率)参见 poc.py 获取带有完整注释的 Python PoC。
PoC 功能简要总结:
A–Z,a–z,0–9,+,/,=)中的每个字符。/api/login/sync,并测量中位响应时间。免责声明: 本 PoC 仅供教育目的和负责任的安研使用。请勿对非您拥有或未经明确书面许可的系统使用。
JavaScript 的 !==(和 ===)运算符执行字典序、提前退出的比较。在 apps/server/src/routes/api/login.ts 中有漏洞的行:
if (expectedHash !== givenHash) {
return [400, { message: "Sync login credentials are incorrect..." }];
}
提前退出行为为每个匹配的字节创建了可测量的时序差异:
每个额外匹配的字节会多花费微小的 CPU 时间 δ。在数千个样本中,猜测“正确字节 N”的平均响应时间比猜测“错误字节 N”的响应时间明显更长,从而泄漏足够的信息来逐字符恢复完整的 HMAC 哈希。
向量字符串: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N
成功利用后,攻击者将获得:
对于在 Trilium 知识库中存储敏感个人数据(密码、私密文档、日记)的用户来说,这尤其严重。
修复方案将非恒定时间的 !== 比较替换为 Node.js 内置的 crypto.timingSafeEqual():
之前(有漏洞):
if (expectedHash !== givenHash) {
return [400, { message: "Sync login credentials are incorrect..." }];
}
之后(安全):
import * as crypto from "crypto";
const expectedBuffer = Buffer.from(expectedHash);
const givenBuffer = Buffer.from(givenHash ?? "");
if (expectedBuffer.length !== givenBuffer.length ||
!crypto.timingSafeEqual(expectedBuffer, givenBuffer)) {
return [400, { message: "Sync login credentials are incorrect..." }];
}
crypto.timingSafeEqual() 始终比较每个字节,因此执行时间不依赖于匹配的字节数。时序信号消失。
参见 vulnerable.ts 和 fix.ts 以获取并排代码示例。
如果您运行的是自托管的 Trilium 服务器,请立即升级到 0.101.0 或更高版本。
# Docker 示例
docker pull zadam/trilium:0.101.0
切勿使用 === / !== 比较机密。 JavaScript 的相等运算符不是恒定时间的。任何使用 === / !== 对 HMAC、令牌或密码的比较都是潜在的时序预言机。
在 Node.js 中始终使用 crypto.timingSafeEqual()(或您语言/运行时的等效方法)比较加密值。这是专为此任务设计的标准 API。
时序攻击在网络上是真实存在的。 虽然纳秒级差异在互联网上似乎不可能检测到,但统计技术和足够样本可以从嘈杂的测量中提取出清晰的信号——尤其是在低抖动环境中。
仅靠速率限制不足以缓解。 即使每个 IP 有速率限制,攻击者通过轮换代理或僵尸网络仍可积累足够样本来利用时序差异。
HMAC 验证应与密码比较受到同等关注。 HMAC 哈希是机密。应像处理任何机密值比较那样,假设时序旁路可能被利用。
对加密模式的代码审查至关重要。 该漏洞通过手动审查发现——一行看似无害的代码却带来严重的安全隐患。专门的加密/安全审计有助于及早发现此类问题。
| 日期 | 事件 |
|---|
本仓库根据负责任的披露原则维护,仅供教育和研究目的。
| 猜测 vs. 期望 | 比较的字节数 | 时间 |
|---|
| 错误字节 0 | 1 | ~T |
| 正确字节 0,错误字节 1 | 2 | ~T + δ |
| 正确字节 0–1,错误字节 2 | 3 | ~T + 2δ |
| … | … | … |
| 全部 44 字节正确 | 44 | ~T + 43δ |
| 指标 | 值 | 原因 |
|---|
| 基础分 | 7.4 高 | |
| 攻击向量 | 网络 (N) | 可通过互联网利用 |
| 攻击复杂度 | 高 (H) | 需要大量请求 + 稳定的时序 |
| 所需权限 | 无 (N) | 无需账户 |
| 用户交互 | 无 (N) | 受害者无需任何操作 |
| 范围 | 不变 (U) | 仅影响 Trilium 服务器 |
| 机密性 | 高 (H) | 完整笔记库可读 |
| 完整性 | 高 (H) | 攻击者可写入/修改笔记 |
| 可用性 | 无 (N) | 不包含拒绝服务组件 |
| 2025-12-19 | CVE-2025-68621 由 GitHub Security 保留 |
| 2025-12-21 | 修复 PR #8129 提交 |
| 2025-12-25 | PR 合并;Trilium 0.101.0 发布 |
| 2026-02-06 | CVE 公开发布 |
| 2026-02-09 | CISA ADP 丰富信息添加 |
| 资源 | 链接 |
|---|
| GitHub 安全公告 | GHSA-hxf6-58cx-qq3x |
| 修复 Pull Request | TriliumNext/Trilium#8129 |
| CVE 记录 (CVEProject) | CVE-2025-68621.json |
| CWE-208 | 可观察的时序差异 |
| Trilium Notes 仓库 | TriliumNext/Trilium |