Ldap Injection in Feathersjs with Jwt Tokens
Ldap Injection in Feathersjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability
LDAP Injection is a server-side injection vulnerability that occurs when an attacker can control inputs used to construct LDAP queries without proper sanitization or parameterization. In FeathersJS applications that integrate LDAP for authentication or directory services, this risk exists when user-influenced data (for example, a username or search filter) is concatenated into an LDAP filter string. Even when the application uses JWT Tokens for session or API authentication, LDAP Injection is not automatically prevented.
The presence of JWT Tokens shifts some trust boundaries but does not eliminate injection risks. JWT Tokens typically carry identity claims and may be used to decide whether a request is authenticated, but they do not sanitize data used in LDAP queries. If an endpoint accepts a search term (e.g., to look up a user in an LDAP directory) and embeds that term directly into a filter like (uid=${userInput}), an attacker can supply crafted input such as admin)(objectClass=*) to alter the filter logic. This can lead to unauthorized directory reads, privilege escalation, or bypass of intended access controls. FeathersJS services that rely on custom LDAP adapters or hooks are susceptible if input validation and query building are not strict.
In this combination, the API’s authentication layer (JWT validation) may correctly identify a caller, but the business logic that queries LDAP does not treat that identity’s supplied parameters as untrusted. For example, a FeathersJS hook might extract a username from a validated JWT, then use that username to perform an LDAP search without escaping reserved characters. Attackers can exploit this by providing specially crafted strings designed to change the semantics of the LDAP filter. This demonstrates that JWT Tokens protect transport and identity assertions but do not mitigate injection flaws in downstream directory queries.
Real-world impact can align with the OWASP API Top 10 category ‘Broken Object Level Authorization’ and ‘Injection’, and may map to compliance frameworks such as SOC2 and GDPR when directory data is exposed. Because LDAP servers often hold sensitive identity information, an injection flaw can expose more than authentication data, potentially revealing group memberships or internal directory structure. The vulnerability exists at the intersection of identity handling (JWT) and directory access (LDAP), emphasizing the need to treat all data used in LDAP filters as hostile even when the caller is authenticated.
Jwt Tokens-Specific Remediation in Feathersjs — concrete code fixes
To remediate LDAP Injection in FeathersJS when JWT Tokens are in use, treat all data used to build LDAP filters as untrusted, regardless of the authentication mechanism. Do not rely on JWT validation to sanitize inputs. Use parameterized queries or strict allow-listing, and apply context-aware escaping for LDAP filters. The following examples show secure patterns in a FeathersJS service.
- Use an LDAP library that supports parameterized filters or build filters safely by escaping special characters. For example, with the
ldapjsclient, prefer constructing filters via objects rather than string concatenation:
// Safe: build filter using library-supported structure
const ldap = require('ldapjs');
const client = ldap.createClient({ url: 'ldap://ldap.example.com' });
app.use('users', {
async find(params) {
const { query } = params;
const username = query.username || '';
// Validate and allow-list expected format before using
if (!/^[a-zA-Z0-9._-]+$/.test(username)) {
throw new Error('Invalid username format');
}
const filter = {
'&': [
{ equality: { attribute: 'uid', value: username } },
{ equality: { attribute: 'objectClass', value: 'person' } }
]
};
const res = await new Promise((resolve, reject) => {
client.search('dc=example,dc=com', { filter }, (err, res) => {
if (err) return reject(err);
const entries = [];
res.on('searchEntry', (entry) => entries.push(entry.object));
res.on('end', () => resolve(entries));
});
});
return res;
}
});
- If you must construct filter strings, apply strict escaping for LDAP special characters (parentheses, asterisk, backslash, NUL) using a well-tested function:
function escapeLDAPFilter(input) {
if (typeof input !== 'string') return input;
return input.replace(/[\\*()\x00]/g, (ch) => {
const hex = ch.codePointAt(0).toString(16).toUpperCase();
return `\\${hex.length === 1 ? '0' + hex : hex}`;
});
}
app.use('profiles', {
async get(params) {
const id = params.query.id || '';
const safeId = escapeLDAPFilter(id);
const filter = `(uid=${safeId})`;
// Use client.search with the escaped filter
// ...
}
});
- Ensure JWT-related claims are not directly interpolated into LDAP filters. If you derive a search base from a JWT claim, validate it against a strict pattern and avoid using raw claim values in directory queries.
| Severity | Remediation Approach | When to Use |
|---|---|---|
| High | Parameterized filter objects | Preferred when using ldapjs or compatible clients |
| High | Strict allow-listing + escaping | When filter string construction is unavoidable |
| Medium | Claim allow-listing | When deriving search attributes from JWT claims |