skills/ldap-injection-anti-pattern/SKILL.md
Security anti-pattern for LDAP injection vulnerabilities (CWE-90). Use when generating or reviewing code that constructs LDAP filters, queries directory services, or handles user input in LDAP operations. Detects unescaped special characters in LDAP filters.
npx skillsauth add igbuend/grimbard ldap-injection-anti-patternInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Severity: High
LDAP Injection occurs when user input is insecurely inserted into LDAP queries without escaping special characters. Attackers manipulate query logic through character injection, enabling authentication bypass, unauthorized data access, privilege escalation, and directory structure disclosure.
Never build LDAP filters by concatenating unescaped user input. Special characters alter filter structure and meaning.
# VULNERABLE: Unescaped user input concatenated into LDAP filter
import ldap
def find_user_by_name(ldap_connection, username):
# Username directly inserted into filter string
# Attacker can inject special LDAP characters: '*', '(', ')', '|'
search_filter = f"(uid={username})"
# Attacker input: 'admin*)(uid=*)'
# Resulting filter: '(uid=admin*)(uid=*)'
# Returns unintended records or bypasses security checks
try:
results = ldap_connection.search_s(
"ou=users,dc=example,dc=com",
ldap.SCOPE_SUBTREE,
search_filter
)
return results
except ldap.LDAPError as e:
print(f"LDAP search failed: {e}")
return None
# SECURE: Escape user input before including in filter
import ldap
from ldap.filter import escape_filter_chars
def find_user_by_name_safe(ldap_connection, username):
# Escape all user input to neutralize special characters
# `ldap.filter.escape_filter_chars` handles this securely
safe_username = escape_filter_chars(username)
search_filter = f"(uid={safe_username})"
# Attacker input ('admin*)(uid=*)') escaped to:
# '(uid=admin\2a\28uid=\2a\29)'
# Searches for user with literal, harmless name
try:
results = ldap_connection.search_s(
"ou=users,dc=example,dc=com",
ldap.SCOPE_SUBTREE,
search_filter
)
return results
except ldap.LDAPError as e:
print(f"LDAP search failed: {e}")
return None
# For authentication, use BIND operation instead of search filters
def authenticate_ldap_safe(username, password):
safe_username = escape_filter_chars(username)
user_dn = f"uid={safe_username},ou=users,dc=example,dc=com"
try:
# Bind to directory as user
# Standard, secure credential verification
conn = ldap.initialize("ldap://ldap.example.com")
conn.simple_bind_s(user_dn, password)
conn.unbind_s()
return True # Bind successful, authentication passed
except ldap.INVALID_CREDENTIALS:
return False # Bind failed, invalid credentials
except ldap.LDAPError as e:
print(f"LDAP error: {e}")
return False
Java:
// VULNERABLE: Unescaped user input in LDAP filter
import javax.naming.directory.*;
public User findUser(String username) throws NamingException {
DirContext ctx = getContext();
// Direct concatenation - injection vulnerable!
String filter = "(uid=" + username + ")";
// Attacker input: "admin*)(uid=*)" creates: "(uid=admin*)(uid=*)"
SearchControls controls = new SearchControls();
NamingEnumeration<SearchResult> results =
ctx.search("ou=users,dc=example,dc=com", filter, controls);
return parseUser(results);
}
// SECURE: Use proper escaping or parameterized queries
import javax.naming.directory.*;
import org.apache.directory.api.ldap.model.filter.FilterEncoder;
public User findUserSafe(String username) throws NamingException {
DirContext ctx = getContext();
// Escape special LDAP characters
String safeUsername = FilterEncoder.encodeFilterValue(username);
String filter = "(uid=" + safeUsername + ")";
SearchControls controls = new SearchControls();
NamingEnumeration<SearchResult> results =
ctx.search("ou=users,dc=example,dc=com", filter, controls);
return parseUser(results);
}
// BEST: Use BIND for authentication instead of search
public boolean authenticateSafe(String username, String password) {
String escapedUsername = FilterEncoder.encodeFilterValue(username);
String userDn = "uid=" + escapedUsername + ",ou=users,dc=example,dc=com";
try {
DirContext ctx = new InitialDirContext(
createEnv(userDn, password));
ctx.close();
return true; // Bind successful
} catch (AuthenticationException e) {
return false; // Invalid credentials
} catch (NamingException e) {
throw new RuntimeException("LDAP error", e);
}
}
C# (ASP.NET):
// VULNERABLE: String interpolation in LDAP filter
using System.DirectoryServices;
public User FindUser(string username)
{
using (var entry = new DirectoryEntry("LDAP://dc=example,dc=com"))
using (var searcher = new DirectorySearcher(entry))
{
// Vulnerable to injection!
searcher.Filter = $"(sAMAccountName={username})";
// Attacker: "admin*)(sAMAccountName=*)" bypasses intended logic
SearchResult result = searcher.FindOne();
return ParseUser(result);
}
}
// SECURE: Escape LDAP special characters
using System.DirectoryServices;
using System.Text.RegularExpressions;
public User FindUserSafe(string username)
{
// Escape LDAP special characters: \ * ( ) NUL
string escapedUsername = EscapeLdapFilter(username);
using (var entry = new DirectoryEntry("LDAP://dc=example,dc=com"))
using (var searcher = new DirectorySearcher(entry))
{
searcher.Filter = $"(sAMAccountName={escapedUsername})";
SearchResult result = searcher.FindOne();
return ParseUser(result);
}
}
private string EscapeLdapFilter(string input)
{
return input
.Replace("\\", "\\5c")
.Replace("*", "\\2a")
.Replace("(", "\\28")
.Replace(")", "\\29")
.Replace("\0", "\\00");
}
Node.js (ldapjs):
// VULNERABLE: Template literal with user input
const ldap = require('ldapjs');
function searchUser(username, callback) {
const client = ldap.createClient({ url: 'ldap://ldap.example.com' });
// Direct interpolation - vulnerable!
const filter = `(uid=${username})`;
// Attacker: "admin*)(uid=*)" bypasses filter
client.search('ou=users,dc=example,dc=com', {
filter: filter,
scope: 'sub'
}, callback);
}
// SECURE: Use ldapjs escape function
const ldap = require('ldapjs');
function searchUserSafe(username, callback) {
const client = ldap.createClient({ url: 'ldap://ldap.example.com' });
// Escape LDAP filter characters
const safeUsername = ldap.filters.escape(username);
const filter = `(uid=${safeUsername})`;
client.search('ou=users,dc=example,dc=com', {
filter: filter,
scope: 'sub'
}, callback);
}
// BEST: Use BIND for authentication
function authenticateSafe(username, password, callback) {
const client = ldap.createClient({ url: 'ldap://ldap.example.com' });
const safeUsername = ldap.filters.escape(username);
const userDn = `uid=${safeUsername},ou=users,dc=example,dc=com`;
client.bind(userDn, password, (err) => {
client.unbind();
callback(err ? false : true);
});
}
rg 'search.*\(.*f["\'].*{|search.*\+.*uid=' --type pyrg 'SearchRequest.*\+|DirectorySearcher.*\+' --type csrg 'new LdapQuery.*\+|filter.*concat' --type javarg 'ldap.*search' -A 2 | rg -v 'escape_filter|escape_dn' (Python)rg 'DirectorySearcher|SearchRequest' -A 3 (C#)rg 'ldap\.search|search_s|search_st' --type pyrg 'DirectorySearcher|DirectoryEntry' --type csrg 'LdapContext\.search|DirContext\.search' --type java* (wildcard), ( ) (filter grouping), \ (escape), | (OR), & (AND)admin*)(uid=*) should not bypass authenticationldap.filter.escape_filter_chars in Python) before placing in LDAP filtersdevelopment
Security anti-pattern for Cross-Site Scripting vulnerabilities (CWE-79). Use when generating or reviewing code that renders HTML, handles user input in web pages, uses innerHTML/document.write, or builds dynamic web content. Covers Reflected, Stored, and DOM-based XSS. AI code has 86% XSS failure rate.
development
Security anti-pattern for XPath injection vulnerabilities (CWE-643). Use when generating or reviewing code that queries XML documents, constructs XPath expressions, or handles user input in XML operations. Detects unescaped quotes and special characters in XPath queries.
development
Security anti-pattern for weak password hashing (CWE-327, CWE-759). Use when generating or reviewing code that stores or verifies user passwords. Detects use of MD5, SHA1, SHA256 without salt, or missing password hashing entirely. Recommends bcrypt, Argon2, or scrypt.
development
Security anti-pattern for weak encryption (CWE-326, CWE-327). Use when generating or reviewing code that encrypts data, handles encryption keys, or uses cryptographic modes. Detects DES, ECB mode, static IVs, and custom crypto implementations.