Ldap Injection with Api Keys
How Ldap Injection Manifests in Api Keys
LDAP injection in API key contexts typically occurs when API keys are used as search parameters for LDAP queries, allowing attackers to manipulate the query structure. This vulnerability emerges when API keys are concatenated directly into LDAP filter strings without proper sanitization.
Consider a user authentication endpoint that validates API keys against an LDAP directory:
const ldap = require('ldapjs');
const client = ldap.createClient({
url: 'ldap://ldap.example.com'
});
app.post('/auth', async (req, res) => {
const apiKey = req.body.apiKey;
const filter = `(apiKey=${apiKey})`;
client.search('ou=users,dc=example,dc=com', { filter }, (err, result) => {
if (err) return res.status(500).json({ error: 'auth failed' });
let found = false;
result.on('searchEntry', entry => {
found = true;
});
result.on('end', () => {
if (found) {
res.json({ success: true });
} else {
res.status(401).json({ error: 'invalid credentials' });
}
});
});
});The critical vulnerability here is the unescaped apiKey parameter. An attacker can craft an API key like:
apiKeyValue)(cn=*)(apiKey=*This transforms the LDAP filter into:
(apiKey=apiKeyValue)(cn=*)(apiKey=*)The query now matches any entry with the specified API key OR any entry where cn matches anything, effectively bypassing API key validation.
Another common pattern occurs in attribute-based access control systems where API keys determine resource permissions:
function getResourceAccess(apiKey, resourceId) {
const filter = `(apiKey=${apiKey})(resourceId=${resourceId})`;
// LDAP search using this filter
}An attacker could inject *)(objectClass=* to retrieve all resources accessible to any API key holder.
Api Keys-Specific Detection
Detecting LDAP injection in API key implementations requires both static analysis and runtime scanning. The most effective approach combines code review with automated security scanning.
Static analysis should look for these patterns:
const dangerousPatterns = [
// Direct string interpolation
/\(apiKey=\${[^}]*}\)[^)]*/g,
// Concatenation patterns
/\(apiKey=([^)]*)\)/g,
// LDAP filter construction
/filter\s*=\s*['"`](.*apiKey.*)['"`]/gi
];Runtime detection with middleBrick scans for LDAP injection vulnerabilities by testing API endpoints with specially crafted payloads. The scanner sends requests with payloads designed to trigger LDAP syntax errors or unexpected query results:
const testPayloads = [
// Basic injection attempt
'*)(cn=*
// Time-based LDAP injection
'*)((uid=*))
// Attribute dumping
'*)((|(objectClass=*))
];middleBrick's black-box scanning approach tests these payloads against your API endpoints without requiring credentials or access to source code. The scanner analyzes responses for indicators of successful injection, such as:
- Unexpected HTTP status codes (200 instead of 401)
- Response size variations indicating data leakage
- Timing differences suggesting conditional query execution
- Authentication bypass where unauthorized requests succeed
The scanner also examines OpenAPI specifications for parameters that might be used in LDAP contexts, flagging potential injection points even before they're exploited.
Api Keys-Specific Remediation
The primary defense against LDAP injection in API key contexts is proper input sanitization and parameterized queries. Most LDAP libraries provide mechanisms to safely construct filters.
Using parameterized LDAP filters:
const ldap = require('ldapjs');
const client = ldap.createClient({
url: 'ldap://ldap.example.com'
});
app.post('/auth', async (req, res) => {
const apiKey = req.body.apiKey;
// Use parameterized filter
const filter = ldap.filters.and([
ldap.filters.equals('apiKey', apiKey)
]);
client.search('ou=users,dc=example,dc=com', { filter }, (err, result) => {
if (err) return res.status(500).json({ error: 'auth failed' });
let found = false;
result.on('searchEntry', entry => {
found = true;
});
result.on('end', () => {
if (found) {
res.json({ success: true });
} else {
res.status(401).json({ error: 'invalid credentials' });
}
});
});
});For Node.js applications, the escapeDnValue and escapeFilter functions from ldap-filters provide additional protection:
const { escapeFilter } = require('ldap-filters');
function validateApiKey(apiKey) {
// Escape special LDAP characters
const safeApiKey = escapeFilter(apiKey);
// Additional validation: enforce expected format
if (!/^[a-f0-9]{32,64}$/.test(safeApiKey)) {
throw new Error('Invalid API key format');
}
return safeApiKey;
}
// Usage
const safeApiKey = validateApiKey(req.body.apiKey);
const filter = `(apiKey=${safeApiKey})`;For Java applications using JNDI, use SearchControls with parameterized queries:
import javax.naming.directory.*;
import javax.naming.ldap.*;
public boolean validateApiKey(String apiKey) throws NamingException {
LdapContext ctx = new InitialLdapContext(env, null);
// Use SearchControls with proper timeout
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
controls.setTimeLimit(5000);
// Parameterized filter using JNDI syntax
String filter = "(apiKey={0})";
String[] filterArgs = { apiKey };
NamingEnumeration<SearchResult> results =
ctx.search("ou=users,dc=example,dc=com", filter, filterArgs, controls);
return results.hasMore();
}Additional defenses include implementing API rate limiting to slow down automated injection attempts, using strong API key formats that resist injection (e.g., UUIDs or cryptographically random strings), and monitoring for unusual authentication patterns that might indicate exploitation attempts.
Frequently Asked Questions
How can I test if my API keys are vulnerable to LDAP injection?
What's the difference between LDAP injection and SQL injection in API key contexts?
(attribute=value)) and have different special characters. Both vulnerabilities stem from unescaped user input, but LDAP requires specific escaping functions like escapeFilter rather than SQL's parameterized queries.