Ldap Injection in Express with Basic Auth
Ldap Injection in Express with Basic Auth — how this specific combination creates or exposes the vulnerability
LDAP Injection occurs when an attacker can manipulate LDAP query construction through untrusted input. In an Express application that uses LDAP for authentication with HTTP Basic Auth, the combination of extracting credentials from the Authorization header and building an LDAP filter or DN from those values creates a path for injection if input validation is absent.
Consider an implementation that takes the user-provided username from the Basic Auth header and concatenates it into an LDAP search filter or DN without sanitization. An attacker can supply a username such as (uid=* or )(uid=admin)(uid=* to alter the intended filter logic. Because the search base and filter are built dynamically, the injected syntax can change the scope or matching criteria, potentially returning other user entries or bypassing intended constraints. Even when authentication ultimately binds with the constructed DN, malicious input can lead to over-privileged searches or information disclosure, which may be surfaced indirectly through timing or error messages.
Because middleBrick scans the unauthenticated attack surface, patterns like these are detectable: it performs input validation checks that look for improper handling of user-controlled data in authentication-related endpoints. The tool also includes checks aligned with OWASP API Top 10 and relevant mappings for authentication and authorization flaws, helping to highlight risks where dynamic LDAP query construction intersects with external input.
Basic Auth-Specific Remediation in Express — concrete code fixes
To secure Express endpoints that rely on HTTP Basic Auth and LDAP, avoid building LDAP DNs or filters by string concatenation with raw user input. Instead, treat the username as an identity to be bound or to parameterize a controlled filter, and use strict allow-lists for acceptable characters. Below are two approaches with runnable Express snippets.
Approach 1: Bind-based authentication with parameterized DN. Resolve the user’s DN through a safe mapping (e.g., an internal directory or database) and then attempt a bind. This eliminates injection risk to the search step because the DN is constructed from a trusted mapping rather than direct input.
const express = require('express');
const basicAuth = require('express-basic-auth');
const { ldapClient } = require('./ldap-client'); // configured ldap client
const app = express();
// In-memory user-to-DN map for example; in production this may be a controlled lookup service
const userToDn = {
alice: 'uid=alice,ou=people,dc=example,dc=com',
bob: 'uid=bob,ou=people,dc=example,dc=com'
};
app.get('/profile',
basicAuth({
challenge: true,
authorizer: (username, password) => {
const dn = userToDn[username];
if (!dn) {
return false;
}
return ldapClient.authenticate(dn, password);
}
}),
(req, res) => {
res.json({ user: req.auth.user, message: 'Authenticated' });
}
);
app.listen(3000, () => console.log('Server running on port 3000'));
Approach 2: Parameterized search with strict input validation. If you must perform a search, validate the username against a strict pattern (alphanumeric plus limited symbols), use a parameterized filter, and avoid concatenating the username into the filter string.
const express = require('express');
const basicAuth = require('express-basic-auth');
const { ldapClient } = require('./ldap-client');
const app = express();
const isValidUsername = (username) => {
// Allow only letters, digits, underscore, hyphen; prevent special LDAP characters
return /^[a-zA-Z0-9_\-]+$/.test(username);
};
app.get('/search',
basicAuth({
challenge: true,
authorizer: (username, password) => {
if (!isValidUsername(username)) {
return false;
}
const filter = '(uid=*)';
const searchOptions = {
filter: `(&(uid=${username})(objectClass=inetOrgPerson))`, // username inserted in a controlled way
scope: 'sub',
attributes: ['uid', 'mail']
};
// Example ldapClient.search usage; actual implementation depends on your LDAP client
return ldapClient.searchAndBind(username, password, searchOptions);
}
}),
(req, res) => {
res.json({ user: req.auth.user, message: 'Search succeeded' });
}
);
app.listen(3000, () => console.log('Server running on port 3000'));
Additional remediation guidance includes using environment-based configuration for connection parameters, enforcing TLS for LDAP traffic, and applying the principle of least privilege to the LDAP service account used for binding. These measures reduce the impact of any residual misconfiguration and align with secure authentication patterns recommended for production services.