CVE-2026-33419:LDAP STS 用户枚举与限流链
状态: 已发布,经历两轮后续修正
首个包含版本: RELEASE.2026-04-17T00-00-00Z
完整修正版本: RELEASE.2026-06-18T00-00-00Z
GitHub Issue: pgsty/minio#23
核心漏洞很直接:LDAP STS 对“用户不存在”和“密码错误”返回不同结果,形成 username oracle。第一版修复统一外部错误,并增加 source IP 与 username 双重限流;连续复核却发现,成功退款、可伪造来源 header、reservation accounting 和共享 username bucket 都可能让安全控制本身成为新的攻击面。
六月的最终方案删除了会造成精确账户锁定的 username bucket,只保留 source IP bucket,并把代理来源识别写成明确的部署契约。
初始威胁模型
入口是 AssumeRoleWithLDAPIdentity。攻击者不需要已有 MinIO 账号,只要能够访问 LDAP STS endpoint,就可以比较 unknown user 与 wrong password 的 code、status 或 message,逐步枚举有效用户名,再结合 password spraying、组织结构猜测或社工攻击。
修复也不能简单把所有错误都伪装成“密码错”。LDAP connection、lookup bind 或目录服务故障必须继续表现为基础设施错误,否则运维会失去诊断能力。
第一轮:统一响应并增加 limiter
2026-04-15 的初始修复做了三件事:
- unknown user 与 bad password 对外返回同一 STS auth error;
- LDAP infrastructure error 仍返回 500,并在 server log 保留真实原因;
- 新增 in-memory limiter,最初同时按 source IP 与 normalized username 分桶。
这一版关闭了内容侧信道,也给暴力尝试增加了成本,但 limiter 的状态机与来源识别随后暴露出更多问题。
第二轮:成功、来源与会计
4 月 16 日的连续修正处理了三类缺陷:
- 成功认证不应消耗失败额度,reserve/commit/cancel/refund 生命周期必须明确;
- 默认只能使用 socket peer,不能直接信任
X-Forwarded-For、X-Real-IP或Forwarded; - refund 与 capacity 必须有边界,避免 cancel 逻辑凭空增发 token。
trusted proxy 需要显式 allowlist,而不是因为请求带着“真实 IP” header 就自动获得信任。
第三轮:删掉 username bucket
六月的对抗性复核推翻了“source + username 一定比 source-only 更强”的直觉。共享 username bucket 可以被任意来源持续耗尽,攻击者只需要低频请求就能在合法用户真正执行 LDAP bind 之前,精确锁死一个目标账户。
最终修复因此:
- 删除 per-username bucket;
- XFF 从右向左剥离 trusted hops,取第一个非可信地址;
- 拒绝
0.0.0.0/0与::/0这类 trusted-proxy footgun; Forwarded不再用于安全敏感分桶;X-Real-IP只在代理覆盖而非透传客户端输入的契约下使用。
这次转折说明,安全控制必须拥有自己的威胁模型。限制更多维度,不等于更安全。
被否决的方案
| 方案 | 否决原因 |
|---|---|
| 为未知用户执行 dummy bind | 放大 LDAP 压力并引入易错的第二条认证路径;内容侧信道已经关闭 |
IPv6 统一按 /64 分桶 | 会让同一站点或运营商前缀下的合法用户互相误伤 |
| XFF 直接取最左值 | 客户端可伪造 |
XFF 与 X-Real-IP 不一致就回退 peer | 攻击者可故意制造不一致,把代理后的所有用户压入同一 bucket |
完整支持 RFC 7239 Forwarded | 安全解析复杂度高,现实收益不足 |
验证与发布
历史记录覆盖 limiter reserve/commit/cancel/refund、并发、success、infra failure、unknown user/bad password 外部等价,以及 RemoteAddr、spoofed header、trusted proxy、多 hop 与 catch-all CIDR。focused package test 与 build 均有记录。
LDAP security e2e 在缺少 _MINIO_LDAP_TEST_SERVER 时会 skip,所以外层 ok 不能冒充真实 LDAP 全场景证明。
初始公开修复提交为 6619d0c,后续修正包括 c55b52c、817a457、084a154 与 5e40665。
最终代价与残余风险
- limiter 最终只按 source IP,放弃跨来源的单账号 hard throttle;
- limiter 是 per-node、in-memory,不是集群全局密码防护;
- botnet、分布式来源、IPv6 地址轮换与 LDAP bind timing 仍然存在;
- trusted proxy 配置错误仍会破坏来源归属;
Forwarded-only 部署会退化为 peer bucket,粒度更粗。
限流只能降低单一来源的尝试速率。真正隐藏 username existence 的,是统一的外部认证响应。