LDAP 인젝션이란?
LDAP(Lightweight Directory Access Protocol) : 조직이나 기업에서 내부 자원(파일, 장치 등) 등의 위치를 찾고 조작할 수 있게 하는 소프트웨어 프로토콜
공격자가 외부 입력으로 의도하지 않은 LDAP 명령어를 수행할 수 있다. 응용 프로그램이 사용자 입력 값에 대한 적절한 필터링 및 유효성 검증을 하지 않아 공격자는 로컬 프록시를 사용함으로 LDAP 문의 변조가 가능하다.
공격 성공 시 승인되지 않은 쿼리에 권한을 부여하고, LDAP 트리 내의 내용 수정이나 임의의 명령 실행을 가능하게 하므로 적절한 필터링 로직을 구현하여야 한다.
LDAP 인젝션 점검
점검 내용 | 웹페이지 내 LDAP 인젝션 취약점 점검 |
점검 목적 | 취약한 시스템에 신뢰할 수 없는 LDAP 코드 삽입 공격을 통한 비인가자의 악의적인 행위를 차단하기 위함 |
점검 대상 | 웹 애플리케이션 소스코드, 웹 방화벽 |
1. 사용자 입력 값에 변조된 LDAP 쿼리 삽입 후 실행되는지 확인

웹사이트의 사용자 인수 값을 입력받는 애플리케이션(폼필드, URL 등)에 변조된 LDAP 쿼리 )(&))를 전송하여 실행되는지 확인해 보았다.

실행이 되지 않았다.
보안 설정 방법
- 사용자 입력 값을 화이트 리스트로 지정하여 영문(a-z, A-Z)과 숫자(0-9)만을 허용한다.
- DN과 필터에 사용되는 사용자 입력 값에는 특수문자가 포함되지 않도록 특수문자를 제거한다.
- 특수문자를 사용해야 하는 경우 특수문자(DN에 사용되는 특수문자는 '\', 필터에 사용되는 특수문자는 =, +, <, >, #, ;, \ 등)에 대해서는 실행 명령이 아닌 일반문자로 인식되도록 처리한다.
- 웹 방화벽에 LDAP 관련 특수문자를 필터링하도록 룰셋을 적용한다.
필터링 대상
‘ | “ | -- | # | ( | ) |
= | */ | /* | + | < | > |
user_tables | user_table_columns | table_name | column_name | Syscolumns | |
union | select | insert | drop | update | and |
or | If | join | substring | from | where |
declare | substr | openrowset | xp_ | sysobject | % |
* | ; | & | | |
코드 예제
userSN과 userPassword 변수의 값으로 *을 전달할 경우 필터 문자열은 “(&(sn=S*)(userPass word=*))”가 되어 항상 참이 되며 이는 의도하지 않은 동작을 유발시킬 수 있다.
private void searchRecord(String userSN, String userPassword) throws NamingException {
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
try {
DirContext dctx = new InitialDirContext(env);
SearchControls sc = new SearchControls();
String[] attributeFilter = { "cn", "mail" };
sc.setReturningAttributes(attributeFilter);
sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
String base = "dc=example,dc=com"; //userSN과 userPassword 값에 LDAP필터를 조작할 수 있는 공격 문자열에 대한 검증이 없어 안전하지 않다.
String filter = "(&(sn=" + userSN + ")(userPassword=" + userPassword + "))";
NamingEnumeration<?> results = dctx.search(base, filter, sc);
while (results.hasMore()) {
SearchResult sr = (SearchResult) results.next();
Attributes attrs = sr.getAttributes();
Attribute attr = attrs.get("cn");
......
}
dctx.close();
} catch (NamingException e) { … }
}
검색을 위한 필터 문자열로 사용되는 외부의 입력에서 위험한 문자열을 제거하여 위험성을 부분적으로 감소시킬 수 있다.
private void searchRecord(String userSN, String userPassword) throws NamingException {
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
try {
DirContext dctx = new InitialDirContext(env);
SearchControls sc = new SearchControls();
String[] attributeFilter = { "cn", "mail" };
sc.setReturningAttributes(attributeFilter);
sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
String base = "dc=example,dc=com";
// userSN과 userPassword 값에서 LDAP 필터를 조작할 수 있는 문자열을 제거하고 사용
if (!userSN.matches("[\\w\\s]*") || !userPassword.matches("[\\w]*")) {
throw new IllegalArgumentException("Invalid input");
}
String filter = "(&(sn=" + userSN + ")(userPassword=" + userPassword + "))";
NamingEnumeration<?> results = dctx.search(base, filter, sc);
while (results.hasMore()) {
SearchResult sr = (SearchResult) results.next();
Attributes attrs = sr.getAttributes();
Attribute attr = attrs.get("cn");
......
}
dctx.close();
} catch (NamingException e) { … }
}