LDAP Injection
LDAP注入
描述
LDAP注入(LDAP Injection)涉及利用使用用户输入生成LDAP语句的基于Web的应用程序。在应用程序未能正确清理用户输入的情况下,攻击者可以通过将特殊关键字注入查询来操纵LDAP语句,这种攻击类似于SQL注入。
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.*;
public class LDAPSearchExample {
public static void main(String[] args) {
// 来自用户的输入(请将其替换为您自己的输入机制)
String userInput = "username"; // 用户提供的输入
// LDAP连接参数
String ldapURL = "ldap://your-ldap-server:389";
String baseDN = "ou=users,dc=example,dc=com"; // 替换为您的基本DN
// 设置用于创建初始上下文的环境
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapURL);
try {
// 创建初始上下文
LdapContext ctx = new InitialLdapContext(env, null);
// 指定搜索过滤器
String searchFilter = "(uid=" + userInput + ")";
// 设置搜索控件
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
// 执行搜索
NamingEnumeration<SearchResult> results = ctx.search(baseDN, searchFilter, searchControls);
// 遍历搜索结果
while (results.hasMore()) {
SearchResult result = results.next();
// 根据需要处理结果(例如,打印属性)
Attributes attributes = result.getAttributes();
System.out.println("User found: " + attributes.get("cn").get());
}
// 关闭上下文
ctx.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
const ldap = require('ldapjs');
const userInput = 'user123';
const ldapQuery = `(uid=${userInput})`; // 容易受到LDAP注入攻击
const client = ldap.createClient({
url: 'ldap://example.com:389'
});
client.search('ou=users,dc=example,dc=com', {
filter: ldapQuery,
}, (err, res) => {
// 处理搜索结果
res.on('searchEntry', (entry) => {
// 处理条目
});
res.on('error', (err) => {
console.error('Error:', err.message);
});
});
<?php
// 容易受到LDAP注入攻击的PHP代码
$userInput = 'user123';
$ldapQuery = "(uid=$userInput)"; // 容易受到LDAP注入攻击
$ldapconn = ldap_connect("ldap://example.com");
ldap_bind($ldapconn, "cn=admin,dc=example,dc=com", "adminpassword");
$result = ldap_search($ldapconn, "ou=users,dc=example,dc=com", $ldapQuery);
$entries = ldap_get_entries($ldapconn, $result);
// 处理搜索结果
foreach ($entries as $entry) {
// 处理条目
}
ldap_close($ldapconn);
?>
建议
为了降低LDAP注入的风险,请考虑以下建议:
-
参数化查询: 使用参数化查询而不是直接拼接来构建LDAP查询。
-
输入验证 / 清理: 实施输入验证以确保用户输入符合预期格式,并且不包含可能改变LDAP查询的特殊字符,还可以使用
LdapFilterEncode等方法清理用户输入。 -
使用自动防止LDAP注入的框架: 利用完善且安全的LDAP查询API或库,在构建LDAP查询时自动对用户输入进行编码。
-
启用绑定认证: 如果攻击者设法执行LDAP注入,使用绑定认证(Bind Authentication)将限制LDAP服务器的攻击面。
-
最小特权: 最小特权原则是一个经验法则,旨在最大限度地减少受损服务的影响。
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.*;
public class LDAPSearchMitigatedExample {
public static void main(String[] args) {
// 来自用户的输入(请将其替换为您自己的输入机制)
String userInput = "username"; // 用户提供的输入
// LDAP连接参数
String ldapURL = "ldap://your-ldap-server:389";
String baseDN = "ou=users,dc=example,dc=com"; // 替换为您的基本DN
// 设置用于创建初始上下文的环境
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapURL);
try {
// 创建初始上下文
LdapContext ctx = new InitialLdapContext(env, null);
// 使用参数化查询来避免LDAP注入
String searchFilter = "(uid={0})";
Object[] filterArgs = {userInput};
// 设置搜索控件
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
// 执行搜索
NamingEnumeration<SearchResult> results = ctx.search(baseDN, searchFilter, filterArgs, searchControls);
// 遍历搜索结果
while (results.hasMore()) {
SearchResult result = results.next();
// 根据需要处理结果(例如,打印属性)
Attributes attributes = result.getAttributes();
System.out.println("User found: " + attributes.get("cn").get());
}
// 关闭上下文
ctx.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 包含输入验证和参数化查询的缓解后的JavaScript代码
const ldap = require('ldapjs');
const userInput = 'user123';
// 执行输入验证以确保userInput对于LDAP查询是安全的
if (isValidUserInput(userInput)) {
const ldapQuery = `(uid=${userInput})`;
const client = ldap.createClient({
url: 'ldap://example.com:389'
});
client.search('ou=users,dc=example,dc=com', {
filter: ldapQuery,
}, (err, res) => {
// 处理搜索结果
res.on('searchEntry', (entry) => {
// 处理条目
});
res.on('error', (err) => {
console.error('Error:', err.message);
});
});
} else {
console.log('Invalid userInput');
}
function isValidUserInput(userInput) {
// 实施适当的输入验证逻辑
// 例如,检查允许的字符、长度等。
return /^[a-zA-Z0-9]+$/.test(userInput);
}
<?php
// 包含输入验证和参数化查询的缓解后的PHP代码
$userInput = 'user123';
// 执行输入验证以确保userInput对于LDAP查询是安全的
if (isValidUserInput($userInput)) {
$ldapQuery = "(uid=$userInput)";
$ldapconn = ldap_connect("ldap://example.com");
ldap_bind($ldapconn, "cn=admin,dc=example,dc=com");
$result = ldap_search($ldapconn, "ou=users,dc=example,dc=com", $ldapQuery);
$entries = ldap_get_entries($ldapconn, $result);
// 处理搜索结果
foreach ($entries as $entry) {
// 处理条目
}
ldap_close($ldapconn);
} else {
echo 'Invalid userInput';
}
function isValidUserInput($userInput) {
// 实施适当的输入验证逻辑
// 例如,检查允许的字符、长度等。
return preg_match('/^[a-zA-Z0-9]+$/', $userInput);
}
?>
链接
标准
- GDPR:
- ART_25
- ART_32
- PCI_STANDARDS:
- REQ_6_2
- REQ_6_3
- REQ_6_4
- REQ_6_5
- SOC2_CONTROLS:
- CC_2_1
- CC_4_1
- CC_7_1
- CC_7_2
- CC_7_4
- CC_7_5
- HIPAA_CONTROLS:
- SECURITY212
- SECURITY213
- SECURITY255