安全でないLDAP認証(平文送信)

平文でのLDAP simpleバインド

説明

LDAPの simple 認証で、TLSや適切なSASLの保護を適用していない場合、ユーザー名とパスワードは平文で送信されます。ldap:// 接続でも、バインド前にStartTLSを適用すれば保護できます。通信を傍受できる攻撃者は、盗聴や中間者攻撃で認証情報を盗み、アカウントの乗っ取り、権限昇格、ほかの内部システムへの侵入に悪用する可能性があります。

想定される影響

  • 認証情報の窃取: 平文のユーザー名やパスワードをネットワーク上で傍受されるおそれがあります。
  • 不正アクセスと権限昇格: 盗まれたアカウントでAD/LDAPリソースにアクセスされ、追加の権限を得られる可能性があります。
  • 横展開: 同じ認証情報を使うほかの内部システムにも侵入されるおそれがあります。
  • セキュリティポリシー違反: パスワードの暗号化送信を求める組織の要件を満たせない可能性があります。
  • 認証情報の再利用: 取得されたsimpleバインドの認証情報を、繰り返しログインに使われるおそれがあります。

対処方法

  • ldaps:// を使い、TLSで通信を暗号化します。通常はポート636を使用します。
  • サーバーが対応している場合は、認証前にStartTLSで ldap:// 接続をTLSに切り替えます。
  • 適切な環境ではSASLを使用します。Kerberos/JAASを正しく構成したうえで、GSSAPIと auth-conf などの適切な保護レベル(QOP)を指定します。新しい構成では、廃止されたDIGEST-MD5を使わないでください。
  • TLSなどで保護されていない接続では、simpleバインドを禁止します。
  • 信頼する証明書のストアを構成し、ホスト名検証を有効にして通信の傍受を防ぎます。
  • サービスアカウントの権限を必要最小限にし、適切なパスワード更新や認証失敗回数の制限を行います。

例

変更前

java
import javax.naming.Context;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import java.util.Hashtable;

public class InsecureLdapLogin {
    public DirContext login(String user, String pass) throws Exception {
        Hashtable<String, String> env = new Hashtable<>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, "ldap://10.0.0.5:389"); // 平文接続
        env.put(Context.SECURITY_AUTHENTICATION, "simple");  // Simple Bind
        env.put(Context.SECURITY_PRINCIPAL, "uid=" + user + ",ou=people,dc=corp,dc=local");
        env.put(Context.SECURITY_CREDENTIALS, pass);
        return new InitialDirContext(env); // 認証情報が平文で送信される
    }
}

変更後

java
import javax.naming.Context;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.ldap.Rdn;
import java.util.Hashtable;

public class SecureLdapLogin {
    public DirContext login(String user, char[] pass) throws Exception {
        Hashtable<String, Object> env = new Hashtable<>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, "ldaps://ldap.corp.local:636"); // TLSで暗号化した接続
        env.put(Context.SECURITY_AUTHENTICATION, "simple"); // TLS上で実行
        String userDn = "uid=" + Rdn.escapeValue(user)
                + ",ou=people,dc=corp,dc=local";
        env.put(Context.SECURITY_PRINCIPAL, userDn);
        env.put(Context.SECURITY_CREDENTIALS, new String(pass));
        // 承認されたCAを含むJVMのtruststoreを使用し、endpoint identificationを無効にしないでください。
        return new InitialDirContext(env);
    }
}

// 別の方法: Kerberos/JAASを構成した環境でのSASL GSSAPIの例
// env.put(Context.PROVIDER_URL, "ldap://ldap.corp.local:389");
// env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
// env.put("javax.security.sasl.qop", "auth-conf"); // 完全性と機密性の保護

説明:

  • 変更前: TLSなどで保護していない ldap:// 接続でsimpleバインドを行うため、パスワードが平文で送信されます。
  • 変更後: ldaps://、信頼する証明書のストア、ホスト名検証によってTLS接続を保護します。ユーザー名は Rdn.escapeValue でDNの属性値としてエスケープします。Kerberos環境では、廃止されたDIGEST-MD5の代わりにGSSAPIを単一の認証メカニズムとして選び、auth-conf などのQOPを明示できます。

参考資料