[log4j 코드리뷰] JNDI에 대해

이상·2022년 9월 15일
0

log4j 총정리보고서

목록 보기
2/6

JNDI란?

JNDI는 Java Naming and Directory Interface의 약자로 Java 애플리케이션이 네트워크에서 객체를 찾고 가져올 수 있도록 하는 표준 API이다. 응용프로그램은 JNDI를 호출하여 자원과 다른 프로그램 객체를 찾는다. 모든 자원 객체는 사용자에게 친숙하고 고유한 JNDI로 식별되며 이 자원의 객체와 JNDI 이름은 애플리케이션 서버에 포함된 이름지정 및 디렉토리 서비스에 의해 함께 바인딩된다.

Java 애플리케이션은 JNDI로 네이밍 및 디렉토리 서비스에 접근한다. JNDI를 사용하려면 JNDI 클래스와 하나 이상의 서비스 프로바이더가 있어야 하는데 이 예시로는 LDAP, CORBA, JAVA RMI 등이 있다.

원래 사용용도

Java 애플리케이션을 주소 데이터베이스나 LDAP 서버같은 외부 디렉토리 서비스에 연결할 때 사용한다. 데이터베이스가 Oracle 등으로 바뀔 가능성이 있거나 JDBC 설정이 바뀔 필요가 있을 때 JNDI를 이용하면 프로그래머나 DB 관리자는 편리하게 데이터베이스를 이용할 수 있다.

취약점 발생부분 코드리뷰

JNDILookup 클래스에서 취약점이 발생한다.


package org.apache.logging.log4j.core.lookup;

import java.util.Objects;

import javax.naming.NamingException;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.net.JndiManager;
import org.apache.logging.log4j.status.StatusLogger;

# JNDI 자원에서 lookup key

@Plugin(name = "jndi", category = StrLookup.CATEGORY)
public class JndiLookup extends AbstractLookup { # AbstractLookup이 JndiLookup 상속
#final 필드는 초기값이 저장되면 최종값이 됨
private static final Logger LOGGER = StatusLogger.getLogger(); #statusLogger(로깅 시스템에서 발생하는 이벤트 기록) 상태 검색
    private static final Marker LOOKUP = MarkerManager.getMarker("LOOKUP"); #LOOKUP이 나올 때 marker 검색
    static final String CONTAINER_JNDI_RESOURCE_PATH_PREFIX = "java:comp/env/"; # JNDI 자원 나올 때 자원의 맨 앞에 경로는 저걸로 고정
 
    public JndiLookup() {
        if (!JndiManager.isJndiLookupEnabled()) { #JMS(Java Message Service)에 대한 JNDI 시스템 속성이 현재 활성화되어 있는지 테스트
            throw new IllegalStateException("JNDI must be enabled by setting log4j2.enableJndiLookup=true"); # throw가 에러를 발생시켜라, IllegalStateExeption이 부적절한 시기에 메소드가 호출되었음을 알림
        }
    }
    
    @Override
    public String lookup(final LogEvent event, final String key) {
        if (key == null) {
            return null;
        }
        final String jndiName = convertJndiName(key); 
        try (final JndiManager jndiManager = JndiManager.getDefaultManager()) { # InitialContext 써서 기본 JndiManager 가져옴
            return Objects.toString(jndiManager.lookup(jndiName), null); 
	# getClass().getName() + '@' + Integer.toHexString(hashCode())
        } catch (final NamingException e) {
            LOGGER.warn(LOOKUP, "Error looking up JNDI resource [{}].", jndiName, e);
            return null;
        }
    }
private String convertJndiName(final String jndiName) {
        if (!jndiName.startsWith(CONTAINER_JNDI_RESOURCE_PATH_PREFIX) && jndiName.indexOf(':') == -1) { # 맨 앞에가 java:comp/env/로 시작하지 않고, :가 없을 때(-1) java:comp/env/에다가 jndiName 붙인것 반환해라
            return CONTAINER_JNDI_RESOURCE_PATH_PREFIX + jndiName;
        }
        return jndiName; # 맨 앞에 java:comp/env/ 있고 : 있으면 jndiname이 이미 완전한 형태이므로 jndiName 반환
    }
}
profile
중앙대학교 산업보안학과 정보보호동아리 이상입니다.

0개의 댓글