logo

Database

Need

Prevention of LDAP injection by validating and escaping values placed in search filters

Context

• Usage of Rust 1.75+ for building application services

• Usage of the actix-web crate for handling HTTP requests

• Usage of the ldap3 crate for directory queries

Description

1. Non compliant code

use actix_web::{get, web, HttpResponse};
use ldap3::{LdapConn, Scope};
use serde::Deserialize;

#[derive(Deserialize)]
struct SearchQuery {
    uid: String,
}...

The `find_user` handler below builds an LDAP search filter with `format!("(uid={})", query.uid)` and passes it to `LdapConn::search`. LDAP filters have their own syntax, in which `*`, `(`, `)`, `\` and the NUL byte are special. Because the `uid` parameter is inserted without escaping, the caller can change the meaning of the filter. A `uid` of `*` matches every entry in the directory, and a value such as `admin)(|(objectClass=*` adds new conditions to the query. When the filter drives an authorization or login decision, this lets an attacker bypass the check. When the results are returned, it lets them enumerate users and read attributes they should not see.

2. Steps

• Escape every request-derived value with `ldap3::ldap_escape` before inserting it into a search filter.

• Validate identifiers such as `uid` or `mail` against a strict character set and maximum length before escaping them.

• Escape values placed in distinguished names with `ldap3::dn_escape`, which follows the RFC 4514 rules instead of the filter rules.

• Request only the attributes the endpoint needs, and bind with a service account that can read no more than that.

• Do not use the number of entries returned by a search as an authentication decision; bind as the user to verify credentials.

3. Secure code example

use actix_web::{get, web, HttpResponse};
use ldap3::{ldap_escape, LdapConn, Scope};
use serde::Deserialize;

#[derive(Deserialize)]
struct SearchQuery {
    uid: String,
}...

The corrected handler applies two controls before the value reaches the filter. First, it validates the format of the `uid`: only ASCII letters, digits, `.`, `-` and `_` are accepted, and the length is capped. Anything else is rejected with `400 Bad Request`, which also removes wildcards that would otherwise allow enumeration. Second, it escapes the value with `ldap3::ldap_escape`, which encodes the filter metacharacters `*`, `(`, `)`, `\` and NUL as `\XX` sequences, as required by RFC 4515. Validation limits what a legitimate identifier looks like, and escaping guarantees that whatever passes is treated as data by the directory server.

References

• 107. LDAP injection