안전하지 않은 LDAP 인증 (평문 전송)

LDAP Simple Bind Over Cleartext

설명

LDAP 연결에서 simple 인증을 사용하면서 TLS나 적절한 SASL 보호를 적용하지 않으면 사용자 아이디와 비밀번호가 평문으로 전송됩니다. ldap:// 연결도 바인드 전에 StartTLS를 적용하면 보호할 수 있습니다. 공격자는 같은 네트워크에서 패킷 스니핑이나 중간자(Man-in-the-Middle) 공격을 통해 자격 증명을 탈취할 수 있습니다. 탈취된 자격 증명은 계정 도용, 권한 상승, 추가 내부 시스템 침투로 악용될 수 있습니다.

잠재적 영향

  • 자격 증명 탈취: 네트워크에서 평문으로 노출된 아이디/비밀번호를 스니핑으로 가로챌 수 있음
  • 무단 접근 및 권한 상승: 탈취한 계정으로 AD/LDAP 자원에 로그인하여 더 높은 권한을 획득할 수 있음
  • 내부 확산(Lateral Movement): 동일 자격 증명을 다른 시스템에도 재사용하여 내부망으로 침투 확장
  • 보안 정책 위반: 비밀번호의 암호화 전송을 요구하는 조직의 보안 정책을 충족하지 못할 수 있음
  • 세션/재사용 공격: 간단한 바인드 자격 증명을 재사용하여 반복 로그인 공격이 가능

해결 방법

  • ldaps:// 사용: TLS가 적용된 포트를 사용(일반적으로 636)하여 전송 구간을 암호화
  • StartTLS 적용: 서버가 지원할 경우 ldap:// 접속 후 TLS로 업그레이드하여 평문 전송 차단
  • SASL 인증 사용: Kerberos/JAAS가 올바르게 구성된 환경에서는 GSSAPI와 적절한 QOP(auth-conf)를 사용합니다. 폐기된 DIGEST-MD5는 새 구성에 사용하지 않습니다.
  • simple 인증 제한: TLS 등으로 보호되지 않은 채널에서 simple 바인드 금지 정책 적용
  • 인증서 검증: 서버 인증서 신뢰 저장소(truststore) 구성 및 호스트네임 검증 활성화로 MITM 방지
  • 운영 가이드: 서비스 계정 최소 권한 부여, 정기 비밀번호 교체, 실패 시도 제한 등 보안 설정 강화

예시

변경 전

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를 명시할 수 있습니다.

참조