LDAPインジェクション:認証の「門番」を無力化する悪魔の数式
現場でインシデント対応をしていると、「認証ロジックなんて、枯れた技術だし大丈夫だろう」と油断しているプロジェクトによく出くわす。だが、LDAP(Lightweight Directory Access Protocol)を直接クエリに組み込んでいるシステムは、今なお攻撃者にとって「格好の獲物」だ。
LDAPインジェクションは、SQLインジェクションほど騒がれないが、攻撃が成功した時のインパクトは絶大だ。認証バイパス、権限昇格、あるいはディレクトリ情報の全流出。これらは単なるエラーログではなく、ビジネスの終焉を意味する。
今日は、なぜLDAPクエリへの入力が「爆弾」になるのか、そしてどうすれば我々が安眠できるコードを書けるのか、その真髄を叩き込む。
—
1. なぜLDAPクエリは「乗っ取られる」のか
LDAPインジェクションの基本原理は、SQLインジェクションと変わらない。ユーザーからの入力を、クエリの構造を形作る「メタ文字」として解釈させてしまうことにある。
例えば、ユーザー名を入力するログイン画面で、バックエンドが以下のようなフィルタを生成しているとしよう。
(&(uid=USER_INPUT)(userPassword=PASSWORD_INPUT))
ここで、攻撃者は USER_INPUT に admin)(|(uid=admin と入力する。すると、生成されるフィルタはこうなる。
(&(uid=admin)(|(uid=admin)(userPassword=PASSWORD_INPUT))
このクエリは、「uidがadminであり、かつ(uidがadmin または パスワードが一致する)」という論理構造に変換される。つまり、パスワードが何であれ、uid=admin が合致すれば認証がパスしてしまうのだ。これが、認証バイパスの仕組みだ。
—
2. 対策の鉄則:フィルタリングではなく「ライブラリ」を使え
多くのエンジニアが「( や ) を置換すればいい」と考えがちだが、それは無意味な徒労だ。エンコーディングの不一致や、Unicodeの正規化を突かれた瞬間に回避される。
防御の唯一の正解は、「LDAPフィルタを文字列連結で作成しないこと」、そして「各ライブラリが提供するエスケープ処理を強制すること」だ。
Pythonでの実装例 (python-ldap)
Pythonの ldap3 や python-ldap を使う場合、手動でクエリを構築してはならない。必ずライブラリが提供するエスケープ関数を通すこと。
import ldap3
# ユーザー入力をLDAPフィルタ用に安全にエスケープする関数
def safe_ldap_filter(user_input):
# ldap3ライブラリのescape_filter_charsを使用
# これにより、'(' や ')' などのメタ文字が正しくエスケープされる
return ldap3.utils.conv.escape_filter_chars(user_input)
# セキュアなクエリ構築
username = safe_ldap_filter(user_provided_username)
search_filter = "(&(uid={})(objectClass=person))".format(username)
# このように構築すれば、攻撃者が何を注入しようとしても、
# それは単なる「文字列」として扱われ、クエリの構造を壊すことはできない。
PHPでの実装例 (LdapRecord / Laminas)
PHPでLDAPを扱う場合も、素の ldap_search に文字列をそのまま投げず、フィルタの構築を抽象化するラッパーライブラリ(LdapRecord等)を利用し、常に値のバインドを行うべきだ。
// ラッパーライブラリを使用し、値をバインドするイメージ
$user = $query->where('uid', '=', $userInput)->first();
// もし素のPHP関数を使うなら、必ず ldap_escape を使用する
$username = ldap_escape($userInput, "", LDAP_ESCAPE_FILTER);
$filter = "(&(uid=" . $username . ")(objectClass=inetOrgPerson))";
—
3. インフラ層での「最後の砦」:WAFと設計思想
アプリケーション層での対策が基本だが、防御は多層であるべきだ。WAF(AWS WAF等)を使っているなら、LDAPインジェクションを意図したルールを適用しておく。
- WAFのカスタムルール:
(,),&,|,*などのLDAP特有のメタ文字が連続して含まれるリクエストをブロックするルールを定義する。ただし、これらは正常な入力と競合する可能性があるため、十分なテストが必要だ。- 最小権限の原則 (Principle of Least Privilege):
- アプリケーションがLDAPに接続する際のBindアカウントは、検索のみに限定し、パスワード属性へのアクセス権すら与えてはならない。
- 認証は「Bind認証(LDAPサーバーのBind機能)」を使い、アプリケーション側でパスワードを突き合わせる実装を避けるのが、最も安全なアーキテクチャだ。
—
最後に:エンジニアとしてのマインドセット
いいか、セキュリティ対策は「チェックリストを埋める作業」ではない。「悪意ある第三者が、どうやってこのコードの裏をかくか」を想像するゲームだ。
今日紹介したエスケープ処理は、言わば「鍵のかかったドア」を作る作業だ。だが、ドアの裏側に隠し通路を作っていないか、常に疑うこと。実装が終わったら、「もし自分が攻撃者だったら、この入力をどう変形させるか?」と自問自答してほしい。
コードは嘘をつかない。君たちが書く一行一行が、ユーザーの情報を守る最後の砦になることを忘れないでくれ。困ったことがあればいつでも聞け。我々の仕事は、侵入される前に「突破不可能」な設計を完成させることだ。
コメント