HIGH ldap injectionactixbasic auth

Ldap Injection in Actix with Basic Auth

Ldap Injection in Actix with Basic Auth — how this specific combination creates or exposes the vulnerability

LDAP Injection occurs when user-supplied input is concatenated into an LDAP query without proper validation or escaping. In Actix applications that use HTTP Basic Auth, the username and password extracted from the Authorization header are sometimes reused in backend LDAP calls—for example, to validate group membership or to build a user-specific search filter. When the application does not sanitize these inputs, an attacker can inject LDAP metacharacters such as (, ), &, |, *, or ~ into the credential values, altering the LDAP query logic.

Consider an Actix service that receives a Basic Auth header, extracts the credentials, and then performs an LDAP bind or search using string interpolation. An attacker can supply a username like admin)(uid=*) or a password containing (objectClass=*). These characters change the structure of the LDAP filter, potentially bypassing authentication, enumerating users via partial matches, or extracting attributes depending on the server’s access controls. Because the scan checks Authentication and Input Validation in parallel, findings may surface both an authentication bypass risk and unchecked special characters in credential fields.

In a black-box scenario—where the scanner tests the unauthenticated attack surface—the tool can probe the login or user-info endpoint with crafted Basic Auth strings containing LDAP metacharacters and observe whether the server responds differently (e.g., different status codes, timing differences, or data leakage). Because the credentials flow through the application into LDAP, the attack chain ties together the Authentication check (weak or misapplied auth mechanisms), Input Validation (lack of sanitization or parameterization), and Data Exposure (potential disclosure of user attributes or authentication results). This illustrates why treating credentials as untrusted input is essential even when they are transmitted via Basic Auth.

Basic Auth-Specific Remediation in Actix — concrete code fixes

To mitigate LDAP Injection in Actix when using HTTP Basic Auth, treat the decoded username and password as untrusted input and avoid building LDAP queries through string concatenation. Use parameterized LDAP APIs or an LDAP filter escape mechanism provided by your LDAP client library. Below are concrete Actix examples showing a vulnerable approach and a hardened alternative.

Vulnerable Actix handler using Basic Auth and direct string interpolation

use actix_web::{web, HttpResponse, HttpRequest};
use ldap3::{LdapConnAsync, Scope, SearchEntry};

async fn auth_ldap(req: HttpRequest, body: web::Json) -> HttpResponse {
    let credentials = req.headers().get("Authorization")
        .and_then(|h| h.to_str().ok())
        .and_then(|s| s.strip_prefix("Basic "))
        .and_then(|encoded| base64::decode(encoded).ok())
        .and_then(|b| String::from_utf8(b).ok());

    if let Some(creds) = credentials {
        let parts: Vec<&str> = creds.splitn(2, ':').collect();
        if parts.len() == 2 {
            let (username, password) = (parts[0], parts[1]);
            // Vulnerable: directly embedding user input into LDAP filter
            let filter = format!("(&(uid={})(userPassword={}))", username, password);
            let (conn, mut ldap) = LdapConnAsync::new("ldap://ldap.example.com").await.unwrap();
            let (res, _) = ldap.search("dc=example,dc=com", Scope::Subtree, &filter, vec!["uid"]).await.unwrap();
            // ... handle result
        }
    }
    HttpResponse::Unauthorized().finish()
}

Hardened Actix handler with input validation and parameterized LDAP filter

use actix_web::{web, HttpResponse, HttpRequest};
use ldap3::{LdapConnAsync, Scope, SearchEntry};
use ldap3::result::Result as LdapResult;

/// Basic Auth credentials should be validated and escaped before use in LDAP.
async fn auth_ldap_secure(req: HttpRequest) -> HttpResponse {
    let credentials = req.headers().get("Authorization")
        .and_then(|h| h.to_str().ok())
        .and_then(|s| s.strip_prefix("Basic "))
        .and_then(|encoded| base64::decode(encoded).ok())
        .and_then(|b| String::from_utf8(b).ok());

    let (username, password) = match credentials {
        Some(creds) => {
            let parts: Vec<&str> = creds.splitn(2, ':').collect();
            if parts.len() != 2 { return HttpResponse::BadRequest().body("Invalid credentials"); }
            (parts[0], parts[1])
        },
        None => return HttpResponse::Unauthorized().finish(),
    };

    // Validate input: allow only safe characters for LDAP identifiers
    if !is_safe_username(username) || !is_safe_password(password) {
        return HttpResponse::BadRequest().body("Invalid characters in credentials");
    }

    // Use parameterized construction or an escaping function provided by the LDAP library
    let filter = format!("(&(uid={})(userPassword={}))",
        ldap_escape::filter(&username),
        ldap_escape::creds(&password)
    );

    let (conn, mut ldap) = match LdapConnAsync::new("ldap://ldap.example.com").await {
        Ok(conn) => conn,
        Err(_) => return HttpResponse::InternalServerError().finish(),
    };

    // Perform the search with the constructed filter; handle result safely
    match ldap.search("dc=example,dc=com", Scope::Subtree, &filter, vec!["uid"]).await {
        Ok((res, _)) => {
            for entry in res.success().unwrap().entries {
                let e: SearchEntry = SearchEntry::construct(entry);
                // Process user data
            }
            HttpResponse::Ok().finish()
        },
        Err(_) => HttpResponse::InternalServerError().finish(),
    }
}

fn is_safe_username(s: &str) -> bool {
    // Allow alphanumeric, underscore, hyphen; reject parentheses, asterisks, and other LDAP metacharacters
    s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
}

fn is_safe_password(s: &str) -> bool {
    // For passwords, prefer rejecting control characters and LDAP specials
    !s.chars().any(|c| c.is_control() || "()*&|^~".contains(c))
}

Key remediation points include validating input against a strict allow-list, using library-supplied escaping for LDAP filters, and avoiding any direct interpolation of credentials into query strings. These practices reduce the risk of injection while preserving the functionality of authentication against an LDAP directory.

Frequently Asked Questions

Can an attacker exploit LDAP Injection even when Basic Auth credentials are correct?
Yes. If the application uses the credentials to construct LDAP filters via string concatenation, an attacker can inject metacharacters to alter the filter logic, potentially bypassing authentication or extracting additional data even with valid credentials.
Does middleBrick test for LDAP Injection in authenticated and unauthenticated scenarios?
middleBrick tests the unauthenticated attack surface and can detect LDAP Injection when credentials are accepted as input and reflected in LDAP query behavior. For authenticated probes, integration with your test account can provide deeper coverage; however, the scanner operates without credentials by default.