Programming/Java

log4j 보안 취약점 해결 (logback 으로 쉽게 라이브러리 교체)

고고마코드 2021. 12. 13. 22:11
반응형

log4j는 그 동안에도 이슈가 종종 있었는데 이번 보안 취약점은 심각한 보안 결함이라고 한다.
현재(2021.12.9 ~ 2021.12.13) 사건이 이슈화 되었고 나는 이것을 빠르게 해결하기 위해 log4j 를 Logback으로 변경했다.

Log4j 란?

개발자는 여러 이유로 프로그램에 로그를 기록한다.

이것은 오류를 체크하는 로그일 수도 있고, 단순히 모니터링을 위한 로그일 수도 있다.

Log4j는 이러한 프로그램에 로그를 기록할 수 있도록 도와주는 라이브러리이다.

보안 취약점 원인

이 보안 취약점은 JNDI와 LDAP를 이용한다.

JNDI : JAVA 프로그램이 디렉토리를 통해 데이터(JAVA 객체)를 찾을 수 있도록 하는 서비스

LDAP : 분산 디렉터리 서비스에서 사용자, 시스템, 네트워크, 서비스, 앱 등의 정보를 공유하기 위한 오픈 프로토콜

JNDI의 인터페이스 중 하나가 LDAP이다.

그러므로 JAVA로 실행되는 프로그램은 [JNDI + LDAP] 로 데이터(JAVA 객체)를 찾을 수 있다.

이것을 Log4j 에서는 ${prefix:name} 라는 문법으로 JAVA 객체를 볼 수 있다.

해커는 로그가 기록되는 곳을 찾아 이 문법을 활용해 취약점을 이용할 수 있다.

대응 방안

대응 방안은 나무위키를 첨부했다. (Log4j 보안 취약점 사태 - 나무위키)

  • 가장 안전한 방법은 log4j 를 2.15.0 이상으로 올리는 것이다. 하지만 이 방법은 Java 8이 필요하다. 2.12.1 버전 Java 7을 지원하는 마지막 버전이므로, 2.13.0 이상 버전을 사용중이라면 판올림에는 문제가 없다.

  • log4j 2.10.0 이상 사용 시 다음의 방법 중 한 가지 이상의 방법을 사용한다.
    Java 실행 인자(Arguments) 에 시스템 속성을 추가한다. -Dlog4j2.formatMsgNoLookups=true
    Java 실행 계정의 환경 변수 혹은 시스템 변수로 LOG4J\_FORMAT\_MSG\_NO\_LOOKUPS=true를 설정한다.

  • log4j 2.7.0 이상 사용 시 log4j 설정(log4j.xml 등)에 PatternLayout 속성에 있는 %m 부분을 %m{nolookups} 으로 교체한다.

  • log4j 상기버전 미만일 경우 가장 어려운 상황으로, JndiLookup 클래스와 JndiManager 클래스를 읽지 못하도록 조치해야 한다.
    log4j-core.jar 를 직접 빌드하거나, 자바 프로젝트에 패키지명까지 맞춰가면서 dummy화 시켜야 한다.
    이 방법은 위에 링크한 KISA 인터넷 보호나라에서 쉽게 대응할 수 있는 방안을 제공하니까 반드시 열람하도록 한다.

  • log4j 외에 다른 로깅 모듈(logback)로의 교체를 검토한다.

나는 대응 방안 중 마지막 방법, 다른 로깅 모듈을 사용하는 것으로 결정했다.

logback 으로 교체한 이유는 log4j 는 종종 보안 이슈가 있었고 log4j 보다 우월한 점이 많기에 교체를 결정했다.

logback 교체 방법

dependency 추가

dependencies {
    compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.3.0-alpha5'
    compile group: 'ch.qos.logback', name: 'logback-core', version: '1.3.0-alpha5'
    compile group: 'org.slf4j', name: 'slf4j-api', version: '2.0.0-alpha1'
}

logback 의 기반은 slf4j 이므로 slf4j 도 함께 추가해야 한다.

logback.xml 추가 (spring boot 를 사용한다면 logback-spring.xml)

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <property name="directory" value="c:/javaLog"/>

    <appender name="ROLLING"
        class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${directory}/programname.log</file>
        <rollingPolicy
            class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${directory}/programname-%d{yyyy-MM-dd}.%i.log
            </fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy
                class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <Pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n
            </Pattern>
        </encoder>
    </appender>

    <appender name="STDOUT"
        class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n
            </Pattern>
        </layout>
    </appender>

    <root level="trace">
        <appender-ref ref="ROLLING" />
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

파일은 classpath(잘 모르겠다면 아마 resources일 것이다)에 추가하면 된다.

ROLLING 은 파일을 남기기 위해 추가했고 STDOUT 은 로그를 찍어보기 위해서 추가했다.

directory 에 로그를 저장하고자 하는 경로를 지정하면 된다. (만약 경로를 만들어도 로그 파일 생성이 안 된다면 쓰기 권한을 부여해보자)

기존에 로그를 사용하던 것처럼 마지막 <root level="?"> 로 로그 수준을 정할 수 있다.

아래부터 높은 레벨이다 (ERROR > WARN > INFO > DEBUG > TRACE)

설정한 레벨 이상의 로그를 출력한다.

ex) INFO 설정 시 INFO, WARN, ERROR 로그가 출력된다.

  • JAVA 소스에 직접 추가해야 하는 경우
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static Logger logger = LoggerFactory.getLogger(Main.class);

    public static void main(String[] args) {
        logger.trace("trace");
        logger.debug("debug");
        logger.info("info");
        logger.warn("warn");
        logger.error("error");
    }
}

logback.groovy? logback-test.xml? logback.xml?

아마 정상적으로 실행하고 로그를 찾아보면 logback.groovy 와 logback-test.xml 은 찾지 못했고 logback.xml 은 찾았다는 기록이 나올 것이다. 오류가 아니다.

[logback.groovy > logback-test.xml > logback.xml] 이 순서로 높은 우선순위를 가진다.

logback은 위 설정파일을 순서대로 검색하며 우선순위가 높은 설정파일을 찾으면 하위 설정 파일은 검색하지 않는다.

단순히 로컬에서 logback-text.xml 을 만들어 임시로 확인할 수도 있고 별도로 개발서버에만 추가하는 설정을 통해 로그를 다르게 추가할 수도 있다.

특정 조건마다 로그를 다르게 작성하고 싶다면 [org.codehaus.janino] 를 활용하면 된다. 분기문을 통해 유연한 로깅 코드를 개발할 수 있다.

logback 매뉴얼

Chapter 1: Introduction (qos.ch)

URL 이동하여 우측의 [Chapter Index] 참고하여 자세한 설명을 확인할 수 있다.

logback 에 대해서 자세히 알아볼 수 있는 블로그를 참조한다.

Logback 이해하기


주말 사이에 심각한 보안 이슈가 있었는데 log4j 를 업데이트해서 해결한 사람도 많을 것이다.
그러나 log4j는 보안 이슈도 종종 나오고 logback 의 이점이 많으므로 이 기회에 바꾸길 잘했다고 생각한다.

반응형