LDAPインジェクション:ディレクトリサービスという「金庫」をこじ開ける盲点
こんにちは。現場の最前線でコードとログを睨み続けているエンジニア諸君。
今日は「LDAPインジェクション」について話そう。SQLインジェクションほど話題に上がることは少ないが、Active DirectoryやOpenLDAPを使っているエンタープライズ環境において、これが突破された時の被害は壊滅的だ。「認証回避」から「組織図の全抽出」まで、攻撃者は君たちのディレクトリ構造を自在に操る力を手に入れてしまう。
教科書的な定義はWikipediaに譲るとして、現場で起きている「なぜ防げないのか」という核心に迫っていこう。
—
1. なぜLDAPインジェクションは「気づかない」のか
LDAPインジェクションの恐ろしさは、SQLiと違って「エラーが表面化しにくい」点にある。SQLなら構文エラーで画面が真っ白になることもあるが、LDAPは検索フィルタの構文さえ合っていれば、たとえ攻撃者の意図通りにクエリが改ざんされていても、アプリケーションは「正常に空のリストを返した」あるいは「不正な条件で検索を実行した」と判断してしまう。
攻撃者が狙う「論理の穴」
典型的なLDAPフィルタは以下の形式だ。
(&(uid=USER_INPUT)(userPassword=PASSWORD_INPUT))
ここで攻撃者が USER_INPUT に admin)(|(& を入力すると、クエリはこうなる。
(&(uid=admin)(|(&)(userPassword=...))
この結果、パスワードチェックの部分が論理的に無効化され、パスワードを知らなくても管理者のセッションを奪取できる。これがインジェクションの典型的なメカニズムだ。
—
2. 実践:セキュアな実装パターン
「エスケープすればいい」というのは誰でも知っている。だが、現場でよく見る悲劇は、自作の置換関数を使って、特定のメタ文字( )をエスケープし忘れるパターンだ。 や (
LDAPの防御において、最も確実なのは「ライブラリが提供する専用のエスケープ関数を必ず通すこと」。言語ごとのベストプラクティスを提示しよう。
PHPの場合:ldap_escape を使え
自作の str_replace で凌ごうとするな。PHPには標準で専用関数がある。
// 安全なLDAPクエリ構築の例
$user_input = $_POST[‘username’];
// LDAP_ESCAPE_FILTER: フィルタ内でのメタ文字を適切にエスケープする
// 最後の引数にtrueを渡すと、NULLバイトなども含めて徹底的に処理される
$safe_user = ldap_escape($user_input, “”, LDAP_ESCAPE_FILTER);
$filter = “(&(uid=” . $safe_user . “)(objectClass=person))”;
// あとは安全に検索を実行するだけ
$search = ldap_search($ldap_conn, $base_dn, $filter);
Pythonの場合:ldap3 ライブラリの活用
PythonでLDAPを扱うなら ldap3 一択だ。このライブラリは適切に設計されており、フィルタ構築を補助する機能がある。
from ldap3.utils.conv import escape_filter_chars
ユーザー入力を安全な形に変換
user_input = “admin)(|(&”
safe_user = escape_filter_chars(user_input)
構築されたフィルタは安全
filter_str = f”(&(uid={safe_user})(objectClass=person))”
print(f”セキュアなフィルタ: {filter_str}”)
出力: (&(uid=admin\29\28|\28&\29)(objectClass=person))
—
3. インフラ層での防御(多層防御の極意)
コード側の修正が最優先だが、万が一の漏れに備えてインフラ側でも「ガードレール」を敷く。
WAFの活用 (AWS WAF / ModSecurity)
LDAPインジェクション特有のメタ文字パターンを正規表現でブロックする。例えば、( や ) が連続して出現するケースは、正当なビジネスロジックではまずあり得ない。
ModSecurityルールの例:
フィルタ内に不正な記号の連続がある場合をブロック
SecRule ARGS “[\(\)\\&\|]{2,}” \
“id:10001,phase:2,deny,status:403,msg:’LDAP Injection Attempt Detected'”
最小権限の原則 (IAM/ACL)
WebアプリケーションがLDAPサーバーに接続する際のアカウント権限を精査してほしい。「ドメイン管理者」権限で接続させていないか?
検索専用の service_account を作成し、特定のOU(Organizational Unit)以外には読み取り権限を与えない。これが、万が一インジェクションを通した時の被害を「全社流出」から「限定的な情報漏洩」に抑えるための最後の砦となる。
—
4. チーフからの教訓
最後に一つだけ伝えておきたい。セキュリティは「知識」ではなく「習慣」だ。
1. 入力は常に汚れていると思え:ldap_escape を忘れたコードをコミットしたら、それはバグではなく「爆弾」を仕込んだと同義だ。
2. ログの監視:LDAPへのクエリログを定期的に確認し、異常な記号が含まれていないか監視する仕組みを作れ。
3. ライブラリを信じろ:自作のセキュリティロジックは、往々にして攻撃者の想定内だ。枯れた公式ライブラリの関数を使うのが、結局一番の近道である。
君たちが書くその一行が、明日誰かの個人情報を守るかもしれない。泥臭く、しかしスマートに。これからもセキュアなシステム構築を頼む。
コメント