LDAPインジェクション:ディレクトリサービスの「裏口」を塞ぐ技術的流儀
エンジニア諸君、日々のアラート対応とデバッグ、お疲れ様。
今日は多くの現場が「死角」にしているLDAPインジェクションについて話そう。
「うちはSQLインジェクション対策は万全だ」と胸を張るエンジニアに限って、LDAP認証のロジックを見ると、ユーザー入力をそのままフィルタ文字列に連結しているケースが驚くほど多い。Active DirectoryやOpenLDAPは、アプリケーションの「鍵」を握る心臓部だ。ここを突破されるということは、権限昇格どころか、組織全体の認証基盤を乗っ取られることを意味する。
1. なぜLDAPインジェクションは「盲点」なのか
SQLiが「データベースのデータ」を狙うのに対し、LDAPiは「認証プロセスそのもの」や「ディレクトリ構造の可視化」を狙う。攻撃者は、特殊文字を注入することでフィルタの論理演算を書き換え、認証をパスしたり、存在しないはずのユーザー情報を引き出したりする。
例えば、ログインフォームのID入力欄に )(uid= を入力されたらどうなるか。
サーバー側のコードが以下のような単純な連結を行っていれば、LDAPクエリはこう変化する。
// 悪意のある入力後のフィルタ文字列
(&(uid=)(uid=)(userPassword=password))
結果として、パスワード認証をバイパスして先頭のユーザーでログインできてしまう。これが、LDAPiが「認証バイパスの特効薬」と呼ばれる所以だ。
2. 「エスケープすればいい」という甘い考えを捨てる
多くの初心者は「特殊文字を置換すれば大丈夫だろう」と考えるが、それは泥沼への入り口だ。LDAPの仕様は複雑で、検索フィルターのコンテキストによってエスケープすべき文字は変わる。
鉄則:自作の置換関数は100%バグる。必ず言語標準や著名なライブラリが提供するエスケープ関数を使うこと。
以下に、実務で即戦力となるセキュアな実装例を記す。
3. 実践:言語別のセキュアなフィルタ実装
Pythonでの実装例(ldap3ライブラリを使用)
PythonでLDAPを扱うなら、ldap3が業界標準だ。ここにはescape_filter_charsという強力な武器がある。
from ldap3.utils.conv import escape_filter_chars
def get_ldap_filter(username, password):
# ユーザー入力を安全にエスケープする
safe_username = escape_filter_chars(username)
# フィルタを構築。連結ではなくテンプレートに安全な値を流し込む
# 注意:パスワードも同様にエスケープを検討すべきだが、
# 本来はLDAPにバインド(認証)する際、フィルタでパスワードを比較する設計自体を見直すべきだ
return f”(&(uid={safe_username})(objectClass=person))”
JavaScript (Node.js) での実装例
ldapjsを使用する場合、ライブラリ側でエスケープを徹底するのが基本だ。
const ldap = require(‘ldapjs’);
// ユーザー入力をLDAPフィルタ用にエスケープする関数
function escapeLDAP(input) {
return input.replace(/[\\() \0]/g, (char) => {
return ‘\\’ + char.charCodeAt(0).toString(16).padStart(2, ‘0’);
});
}
const username = req.body.username;
const filter = (&(uid=${escapeLDAP(username)})(objectClass=person));
// あとはこのフィルタをsearchオプションに渡すだけ
4. アプリの外側で叩き落とす:WAFの役割
コード修正が間に合わないレガシーなシステムや、多層防御を構築したい場合、WAF(AWS WAF等)で不正なパターンを事前に遮断するのも有効だ。
AWS WAFのルール設定(Regex Pattern Setのヒント):
LDAPインジェクションを狙う文字列には、 が多用される。以下の正規表現をベースに、アプリケーションの入力仕様に合わせてチューニングしてほしい。 や ( )
- パターン:
\(\s[\\!&|] - 解説:
(で始まり、直後にスペースや論理演算子(&,|,!, “)が続くパターンは、LDAPフィルタの構造を破壊しようとする攻撃の兆候だ。
5. チーフからの最後のアドバイス
技術的な対策は上記で完璧だが、最も重要なのは「そもそもLDAPフィルタにユーザー入力を直接埋め込まない設計」にすることだ。
最近のアーキテクチャでは、直接LDAPを叩くのではなく、IDプロバイダー(IdP)や認可サービス(OAuth 2.0 / OIDC)を介する設計が主流だ。アプリケーションが直接LDAPを操作する機会を最小限に減らすことが、最大の防御になる。
「動けばいい」コードは、明日には脆弱性の温床に変わる。我々エンジニアの仕事は、コードを動かすことではなく、そのコードが「攻撃者の挑戦を跳ね返し続けること」にある。
現場からは以上だ。各自のプロジェクトで、今すぐフィルタの連結箇所をgrepしてみてくれ。もし「怪しい」と思ったら、すぐにリファクタリングに着手すること。健闘を祈る。
コメント