LDAP 注入怎么发生
登录与查询场景常把用户输入拼进 LDAP 过滤器(Filter)。过滤器有自己的语法,星号、括号、反斜杠等字符一旦由用户控制,就能改变匹配条件:例如把过滤器闭合后再追加条件,可绕过口令校验或枚举目录内容。防护思路与 SQL 注入类似但规则不同:使用库提供的过滤器构建接口或按 RFC 4515 转义。
基本信息
| 适用场景 | 基于 LDAP/AD 的登录、用户查询、通讯录检索 |
|---|---|
| 风险位置 | 过滤器字符串拼接处,如 uid、mail、cn 条件 |
| 需转义字符 | * ( ) \ 以及 NUL |
| 转义依据 | RFC 4515 过滤器字符串规范 |
| 服务侧加固 | 只读绑定账号、限制可访问子树、关闭匿名绑定 |
典型使用场景
- 登录校验:用 uid 与 password 拼接过滤器做认证(高风险写法)
- 用户查询:按用户名模糊检索通讯录
- 权限同步:从目录服务读取组与成员关系
- SSO 集成:应用在网关或代码里自行查询目录
- 接口测试:确认输入中的特殊字符是否被正确转义
风险写法与安全写法
风险写法与安全写法
风险:"(&(uid=" + user + ")(userPassword=" + pwd + "))"
安全:使用 RFC 4515 转义,需处理 * ( ) \ 与 NUL
更好:使用库的过滤器构建接口,避免手工拼接
Java 示例(Spring):
String filter = String.format("(&(uid=%s)(objectClass=person))",
LdapEncoder.filterEncode(user));
# 认证应使用 bind 操作而不是把口令拼进过滤器比较服务侧加固
服务侧加固
查询账号只读,且限制可访问的目录子树(base DN)
关闭匿名绑定
统一错误信息,避免暴露目录结构
对查询结果数量设置上限,防止被用于批量抽取
开启目录服务访问日志,便于追溯异常查询需转义字符对照
| 字符 | 在过滤器中的含义 | 转义写法 |
|---|---|---|
| * | 通配符,匹配任意字符 | \2a |
| ( | 条件起始 | \28 |
| ) | 条件结束 | \29 |
| \ | 转义符自身 | \5c |
| NUL | 字符串终止 | \00 |
LDAP 转义规则与 SQL 完全不同:只按 SQL 的思路过滤引号没有意义,至少要覆盖星号与括号,否则通配符仍可扩大匹配范围。
登录校验更稳妥的方式是先用 bind 操作验证凭据、再用查询账号读取属性,而不是把用户口令拼进过滤器参与比较,后者直接把口令送进了查询语句。