跳转到主要内容

CVE-2026-33419:LDAP STS 用户枚举与限流链演进

从统一认证失败响应,到修正成功退额、代理源地址解析与移除共享用户名分桶:经历三轮迭代收敛的 LDAP STS 加固记录。

状态: 已发布,并经历两轮后续修正
首个包含版本: RELEASE.2026-04-17T00-00-00Z
完整修正版本: RELEASE.2026-06-18T00-00-00Z
修复提交: 3b950f8、18b712d、9e10f6d、f441108、5e40665
GitHub Issue: pgsty/silo#23

AssumeRoleWithLDAPIdentity 的初始漏洞非常直观:LDAP STS 对“用户不存在”与“密码错误”返回不同的错误信息,构成了无需认证即可利用的用户名探测信标(username oracle)。第一版补丁统一了对外的认证失败响应,并按客户端源 IP 与用户名同时设置了双重限流桶。

然而随后的连续设计复核表明:认证成功退额、可伪造的代理转发头、配额预留核算以及跨源共享的用户名分桶,反而会把安全限流器本身变成精确拒绝服务的新攻击面。6 月份的最终方案(5e40665)果断删除了会导致定点账户锁定的用户名分桶,仅保留按源 IP 限流,并将反向代理源地址识别确立为显式的部署契约。

初始威胁模型

受影响入口为 AssumeRoleWithLDAPIdentity。攻击者无需持有任何已有的 S3 或 LDAP 凭据,只要网络可达 LDAP STS 端点,就能通过对比未知用户与错误密码返回的 HTTP 状态码及错误文案,逐一枚举出有效的目录用户名,再配合密码喷洒(Password Spraying)或社工手段发起攻击。

与此同时,修复不能简单把所有异常都伪装成“密码错误”:LDAP 连接断开、查询账号 Bind 失败或目录服务超时必须继续作为 HTTP 500 基础设施故障返回,否则运维人员将失去故障诊断能力。

第一轮:统一外部响应并引入双维度限流

2026-04-15 的第一版修复(3b950f8)完成了三件事:

  • 将“用户不存在”与“密码错误”的外部响应统一为相同的 400 InvalidParameterValue STS 认证错误;
  • LDAP 基础设施故障继续返回 HTTP 500,并在服务端日志中保留底层详细原因;
  • 新增单节点内存限流器,同时按客户端源 IP 与归一化后的 LDAPUsername 两个维度分桶限流。

这一版关闭了响应内容侧信道并抬高了暴力破解成本,但对限流器状态机与客户端 IP 提取逻辑的深入复核很快暴露出新的缺陷。

第二轮:修正成功退额、代理溯源与令牌核算

4 月 16 日的连续补丁(18b712d、9e10f6d、f441108)修复了第一版限流器的三类缺陷:

  1. 显式的预留/提交/取消/退还生命周期(reserve/commit/cancel/refund):认证成功以及 LDAP 基础设施故障必须退还预留令牌,避免正常的高频登录耗尽失败配额。
  2. 默认不信任代理转发头:默认仅使用 TCP 套接字对端地址(RemoteAddr),绝不因为请求携带了 X-Forwarded-For、X-Real-IP 或 Forwarded 就无条件采信,除非直连对端在显式配置的可信代理白名单中。
  3. 退额与容量硬边界:取消与退还逻辑必须严格受限于桶容量上限,防止高并发取消时凭空多退令牌。

第三轮:为什么删掉了 username 分桶

6 月份的第二轮安全复核推翻了“源 IP + 用户名双维度限流一定比单源 IP 限流更安全”的直觉。

由于按用户名划分的限流桶在节点内被所有客户端源地址共享,知道受害者 LDAPUsername 的攻击者可以从任意源 IP(或轮换多个源 IP)持续发送低频错误请求,把目标用户名的配额桶耗尽。这样一来,合法用户的请求在真正进入 LDAP Bind 之前就会被限流器直接拦截,形成低成本的精确定点账户锁定(Account Lockout DoS)。

因此,随 RELEASE.2026-06-18T00-00-00Z 发布的提交 5e40665 做出了五项关键调整:

  • 彻底删除 per-username 分桶,阻断跨源定点锁死特定 LDAPUsername 的攻击路径;
  • 从右向左剥离 X-Forwarded-For 中的可信代理跳数,选取第一个非可信地址作为客户端源 IP;
  • 拒绝 0.0.0.0/0 与 ::/0 等全通配可信代理配置,防止错误配置导致任意客户端伪造源地址;
  • 不再将 RFC 7239 Forwarded 头用于安全敏感限流分桶;
  • 仅在可信代理覆盖写入而非透传客户端输入的契约下使用 X-Real-IP。

这套可信代理源地址解析机制随后被推广至全站 S3 与 Admin 接口,形成 SN-2026-018(MINIO_API_TRUSTED_PROXIES)。

被否决的候选方案

候选方案 否决原因
为未知用户执行一次虚拟 LDAP Bind(dummy bind) 响应内容侧信道已经关闭;额外发起虚拟 Bind 会白白放大上游 LDAP 目录负载并引入第二条易错路径
将所有 IPv6 客户端统一按 /64 前缀分桶 会导致同一园区网或运营商 /64 前缀下的正常用户互相消耗配额、彼此误伤
直接取 X-Forwarded-For 最左侧地址 最左侧字段由客户端任意填写,攻击者可逐次伪造不同 IP 彻底绕过限流
当 X-Forwarded-For 与 X-Real-IP 不一致时回退至套接字对端地址 攻击者可故意注入冲突的请求头强制触发回退,将同一反向代理后的所有合法用户压入同一个限流桶
完整解析 RFC 7239 Forwarded 头 语法解析状态机复杂且边界歧义多,相较 X-Forwarded-For 与 X-Real-IP 无实际安全收益

验证与发布

单元测试套件完整覆盖了限流器 reserve/commit/cancel/refund 状态机、并发安全、认证成功与基础设施错误的配额退还、未知用户与密码错误外部响应等价性,以及直连 RemoteAddr、伪造转发头、单跳/多跳可信代理和拒绝全通配 CIDR(0.0.0.0/0、::/0)等场景。

需要注意,LDAP 安全端到端测试在未设置 _MINIO_LDAP_TEST_SERVER 环境变量时会自动跳过(skip)真实 LDAP Bind 分支;完整验证 LDAP 目录交互时需配置测试 LDAP 服务实例。

最终取舍与残余风险边界

  • 仅按源 IP 限流的代价:移除 username 分桶消除了跨源定点锁死合法账户的隐患,但也意味着不再跨不同源 IP 实施单账号全局失败硬上限。
  • 单节点内存级限流:该限流器为单节点、内存态防护,并非跨集群全局密码防线;针对分布式僵尸网络、IPv6 地址轮换或 LDAP Bind 耗时侧信道,仍需结合上游 WAF 或 LDAP 目录自身的账户保护策略。
  • 反向代理配置要求:部署在反向代理后方时,必须正确配置可信代理白名单(MINIO_API_TRUSTED_PROXIES);若仅使用 Forwarded 头,限流器将回退至按代理对端地址分桶。
  • 统一响应才是防枚举的核心:限流的作用是压低单一来源的尝试速率,而真正隐藏“用户名是否存在”的是统一的外部认证失败响应。