익명 LDAP 연결 (Anonymous LDAP Binding)

익명 LDAP 바인딩에 허용된 디렉터리 접근 권한 점검

설명

LDAP 서버에 익명으로 바인딩하면 서버는 클라이언트의 신원을 확인하지 않습니다. 실제로 읽거나 수정할 수 있는 데이터는 서버의 익명 접근 권한에 따라 달라집니다. 민감한 디렉터리 정보나 변경 작업을 익명 사용자에게 허용하면 정보 유출이나 무단 변경으로 이어질 수 있습니다.

잠재적 영향

  • 민감한 데이터 노출: 익명 사용자가 LDAP 서버에 접근하여 조직의 사용자 정보, 그룹 목록 등을 조회할 수 있습니다.
  • 권한 없는 데이터 조작: 인증 없이 데이터를 수정할 수 있는 경우, 공격자가 권한을 상승시킬 수 있습니다.
  • 서비스 악용: 익명 사용자가 대량의 LDAP 요청을 보내 서비스 거부(DoS) 공격을 수행할 수 있습니다.

해결 방법

  1. 보호가 필요한 연결에는 서버가 지원하는 인증 방식을 명시적으로 설정하세요. "simple" 인증에는 검증된 TLS를 사용하고, "strong" 문자열만으로 안전한 SASL이 구성된다고 가정하지 마세요.
  2. 최소 권한 계정을 사용하고 비밀번호는 비밀정보 저장소나 접근이 제한된 실행 환경에서 주입하세요. 비밀번호가 없거나 비어 있으면 연결을 거부하세요.
  3. 서버에서 익명 사용자의 민감한 데이터 조회·수정 권한을 차단하세요. 클라이언트의 인증 설정만 바꾸어도 서버의 익명 권한이 변경되는 것은 아닙니다.

예시

변경 전

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

public class UnsafeLDAP {
    public static void main(String[] args) 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://ldap.example.org:389");
        env.put(Context.SECURITY_AUTHENTICATION, "none"); // 익명 바인딩 요청

        InitialDirContext ldapContext = new InitialDirContext(env);
    }
}

변경 후

인증서와 호스트 이름을 검증할 수 있는 LDAPS 서버 및 신뢰 저장소가 필요합니다. app-reader는 필요한 조회 권한만 부여한 예시 계정이며, 실제 계정 DN으로 바꾸세요.

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

public class SecureLDAP {
    public static void main(String[] args) throws Exception {
        Hashtable<String, String> env = new Hashtable<>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, "ldaps://ldap.example.org:636");
        env.put(Context.SECURITY_AUTHENTICATION, "simple"); // TLS 연결에서 비밀번호 인증
        env.put(Context.SECURITY_PRINCIPAL, "cn=app-reader,dc=example,dc=org");
        env.put(Context.SECURITY_CREDENTIALS, getLdapPassword()); // 실행 환경에서 비밀번호 로드

        InitialDirContext ldapContext = new InitialDirContext(env);
        ldapContext.close();
    }

    private static String getLdapPassword() {
        // 접근이 제한된 실행 환경에서 주입한 비밀번호를 확인합니다.
        String password = System.getenv("LDAP_PASSWORD");
        if (password == null || password.isEmpty()) {
            throw new IllegalStateException("LDAP password is not set");
        }
        return password;
    }
}

설명:

  • 변경 전: "none"으로 익명 바인딩을 요청합니다. 접근 범위는 서버가 익명 사용자에게 허용한 권한에 따릅니다.
  • 변경 후: 비어 있지 않은 외부 주입 비밀번호로 TLS 연결에서 인증합니다. 환경 변수 자체가 비밀정보 저장소는 아니므로 실행 환경의 접근 권한과 로그도 보호해야 합니다.

참조