Ldap Injection in Feathersjs with Hmac Signatures
Ldap Injection in Feathersjs with Hmac Signatures — how this specific combination creates or exposes the vulnerability
LDAP Injection is an injection attack targeting LDAP servers through untrusted input used to construct LDAP queries. When an application builds LDAP filter strings by concatenating user-controlled data, attackers can manipulate the filter logic to bypass authentication or extract directory data. FeathersJS is a lightweight, extensible framework for building REST and real-time APIs. If a FeathersJS service uses LDAP for authentication and builds filter strings unsafely, the API can be abused.
HMAC Signatures are typically used for request integrity and authentication. A client generates a signature using a shared secret and includes it in headers; the server recomputes the signature and validates it before processing the request. This mechanism helps ensure the request has not been tampered with. However, HMAC does not inherently protect against injection flaws in other parts of the request, such as LDAP queries. If the server-side logic that validates the HMAC then builds LDAP filters using unchecked input, the HMAC verification can create a false sense of security while the LDAP path remains vulnerable.
The combination of FeathersJS, HMAC-based authentication, and unsafe LDAP query construction can expose an LDAP Injection vector. Consider a scenario where the HMAC validates an API key and a timestamp, but the developer uses another user-controlled parameter (e.g., a username or group identifier) directly in an LDAP filter. If that parameter is not sanitized or parameterized, an attacker can embed LDAP metacharacters such as (, ), &, |, !, or wildcard * into the input. For example, an input like admin)(objectClass=*) could alter the filter semantics, potentially returning all entries or bypassing intended access controls. Because the HMAC passes, the request proceeds to the LDAP query stage, where the injection occurs.
In FeathersJS, services often hook into authentication flows. If an LDAP strategy is implemented inside a hook and the filter string is constructed by string concatenation rather than using parameterized interfaces or prepared statements, the vulnerability is live. Attackers might send crafted requests with specially crafted usernames or search bases to manipulate the LDAP filter, retrieve unauthorized entries, or cause errors leading to information disclosure. The presence of HMAC can complicate detection because logs may show valid signatures while the underlying LDAP query is malicious.
To identify this issue in practice, scanning tools can detect LDAP injection-prone patterns in the API surface and correlate them with authentication flows that rely on HMAC. The scan checks for unsafe string assembly in LDAP-related code paths and flags missing input validation or encoding. Even when HMAC is present, the scan verifies whether untrusted data is used directly in directory queries, ensuring the developer understands that HMAC protects integrity, not injection.
Hmac Signatures-Specific Remediation in Feathersjs — concrete code fixes
Remediation centers on ensuring that user-controlled data used in LDAP queries is never concatenated into raw filter strings. Use parameterized LDAP search APIs or an abstraction layer that escapes special characters. In FeathersJS, implement robust input validation and prefer library functions that handle escaping. Do not rely on HMAC to prevent injection; treat it as an authentication layer, not a sanitization mechanism.
Below are concrete code examples for a FeathersJS service that uses LDAP safely while retaining HMAC for request authentication.
const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');
const ldap = require('ldapjs');
const crypto = require('crypto');
const app = express(feathers());
// HMAC verification middleware
function verifyHmac(req, res, next) {
const signature = req.headers['x-api-signature'];
const timestamp = req.headers['x-timestamp'];
const body = req.body;
const secret = process.env.HMAC_SECRET;
if (!signature || !timestamp) {
return next(new Error('Missing authentication headers'));
}
// Prevent replay attacks (simple example)
if (Math.abs(Date.now() - parseInt(timestamp, 10)) > 300000) {
return next(new Error('Request expired'));
}
const computed = crypto.createHmac('sha256', secret)
.update(timestamp + JSON.stringify(body))
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(computed))) {
return next(new Error('Invalid signature'));
}
return next();
}
app.use('/secure', verifyHmac);
// LDAP client setup
const client = ldap.createClient({
url: 'ldap://ldap.example.com'
});
// Safe LDAP search helper using parameterized filter
function safeLdapSearch(base, usernameAttr, usernameValue, callback) {
// Validate and sanitize usernameValue; allow only alphanumeric and limited symbols
if (!/^[a-zA-Z0-9._-]+$/.test(usernameValue)) {
return callback(new Error('Invalid username format'));
}
// Use an array filter with proper escaping provided by ldapjs
const filter = ldap.filter('and').append({ attribute: usernameAttr, value: usernameValue });
client.search(base, { filter }, (err, res) => {
if (err) return callback(err);
const entries = [];
res.on('searchEntry', (entry) => entries.push(entry.object));
res.on('error', callback);
res.on('end', () => callback(null, entries));
});
}
// Example service hook
app.service('users').hooks({
before: {
find: [async context => {
const { username } = context.params.query;
if (!username) {
throw new Error('Username query parameter is required');
}
const base = 'ou=people,dc=example,dc=com';
const results = await new Promise((resolve, reject) => {
safeLdapSearch(base, 'uid', username, (err, data) => {
if (err) return reject(err);
resolve(data);
});
});
context.result = { data: results };
return context;
}]
}
});
app.listen(3030);
In this example, HMAC verification is applied to the route before any LDAP interaction occurs. The LDAP search uses a parameterized filter built with ldap.filter, which properly escapes special characters. Input validation ensures the username matches an allowed pattern, reducing the risk of injection. This approach keeps HMAC focused on integrity while LDAP queries remain safe.
For production use, ensure the LDAP client is configured securely, connections use TLS, and errors are handled without leaking sensitive information. Regularly review hooks and service logic to confirm that user input is never directly interpolated into LDAP filter strings.