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クエリを作成する際にユーザー入力を自動的にエンコードする、確立された安全なLDAPクエリAPIまたはライブラリを使用します。
-
バインド認証を有効にする: バインド認証を使用すると、攻撃者がLDAPインジェクションの実行に成功した場合でも、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