원격 코드 실행 사례
Log4Shell부터 Next.js까지, 바깥에서 보낸 데이터가 서버에서 코드로 실행된 사건들과 그 공통 원리, 대응 순서다.
목차 (9)
데이터가 코드가 되는 지점
원격 코드 실행(RCE, remote code execution)은 바깥에서 보낸 요청 하나로 서버가 공격자가 고른 코드를 실행하게 되는 취약점이다. 서버 프로세스의 권한을 통째로 넘겨주는 셈이라 CVSS 점수가 거의 늘 9점을 넘는다. 아래 사례들은 언어도 프레임워크도 다르지만 원리는 하나다. 프로그램이 데이터로 받은 글자를 어딘가에서 코드처럼 해석했다. 웹 보안 노트의 SQL 인젝션도 같은 뿌리인데, 여기서는 해석기가 데이터베이스가 아니라 언어 런타임 자체라 피해가 더 크다.
| 갈래 | 데이터가 코드가 되는 곳 | 이 글의 사례 |
|---|---|---|
| 역직렬화 | 바이트를 객체로 되살리며 그 객체의 메서드가 불린다 | Commons Collections(2015), React Server Components(2025) |
| 표현식 언어 | 입력이 템플릿·식 평가기에 들어가 계산된다 | Confluence OGNL(2022) |
| 동적 조회·로딩 | 문자열이 가리키는 이름·주소로 객체나 클래스를 찾아 불러온다 | Log4Shell(2021), Spring4Shell(2022) |
| 인자 주입 | 입력이 다른 프로그램의 명령줄 옵션으로 넘어간다 | PHP-CGI(2012, 2024) |
사례마다 무엇이 일어났나 → 왜 그렇게 됐나 → 영향 범위와 고친 판 → 배울 점 순서로 본다. 공격 문자열이나 재현 절차는 싣지 않는다. 막는 법을 이해하는 데는 원리면 충분하다.
| 해 | 사례 | 스택 | CVE | 갈래 | 고친 판(대표) |
|---|---|---|---|---|---|
| 2015 | Commons Collections 가젯 | Java 직렬화 | 라이브러리 CVE 없음(제품별 CVE-2015-4852 등) | 역직렬화 | 3.2.2, 4.1 |
| 2021 | Log4Shell | Java, Log4j 2 | CVE-2021-44228 | 동적 조회·로딩 | 2.17.1(후속 수정 포함) |
| 2022 | Spring4Shell | Java, Spring Framework | CVE-2022-22965 | 동적 조회(바인딩) | 5.3.18, 5.2.20 |
| 2022 | Confluence OGNL | Java, Confluence | CVE-2022-26134 | 표현식 언어 | 7.18.1 등 판별 |
| 2024 | PHP-CGI Best-Fit | PHP, Windows | CVE-2024-4577 | 인자 주입 | 8.3.8, 8.2.20, 8.1.29 |
| 2025 | React Server Components | JavaScript, React·Next.js | CVE-2025-55182 | 역직렬화 | React 19.0.1, 19.1.2, 19.2.1 |
| 2025 | (비교) Next.js 미들웨어 우회 | JavaScript, Next.js | CVE-2025-29927 | 인가 우회(RCE 아님) | 15.2.3 등 판별 |
Log4Shell (2021, Log4j 2)
무엇이 일어났나
2021년 12월 10일 자바 로깅 라이브러리 Apache Log4j 2의 취약점 CVE-2021-44228이 공개됐다. CVSS 10.0이고, CISA는 같은 날 이를 알려진 악용 취약점(KEV) 목록에 올렸다. 로그에 남는 문자열에 특정 꼴의 글자(${jndi:…})가 들어 있으면, 서버가 그 글자가 가리키는 바깥 서버에 접속해 클래스를 받아 실행할 수 있었다. 사용자 이름, 검색어, 요청 헤더처럼 "그냥 기록만 하는" 값이 입구가 됐다.
왜 그렇게 됐나
Log4j 2에는 로그 메시지 안의 ${…}를 실제 값(환경 변수, 시스템 속성 등)으로 바꿔 주는 lookup 기능이 있었다. 그중 JNDI lookup은 이름으로 원격 디렉터리(LDAP 등)에서 객체를 찾아오는데, 그 응답이 다른 곳의 클래스를 불러오도록 가리킬 수 있었다. 설정 파일에서 쓰라고 만든 편의 기능이 로그로 들어온 바깥 데이터에도 적용된 것이 문제였다. 로그 한 줄이 "이 주소에서 코드를 가져와라"는 명령이 됐다.
공격자는 사용자 이름, 검색어, 요청 헤더처럼 서버가 로그에 남길 만한 값에 lookup 꼴의 글자(${jndi:…}, 주소는 비움)를 넣어 보낸다.
영향 범위와 고친 판
피해 범위가 컸던 까닭은 기술보다 위치에 있다. 거의 모든 자바 서버가 로그를 남기고, Log4j는 직접 쓰지 않아도 다른 라이브러리·제품을 통해 딸려 들어오는 전이 의존성(transitive dependency)인 경우가 많았다. 그래서 많은 조직이 "우리가 Log4j를 쓰는가"부터 답하지 못했다. CISA와 여러 나라 기관의 공동 권고 AA21-356A도 첫 순서로 Log4j를 쓰는 자산의 목록을 만들라고 했다.
고치는 일도 한 번에 끝나지 않았다. Apache Logging 보안 페이지의 기록을 따라가면 다음과 같다.
- 2.15.0: 메시지 lookup을 기본으로 끔. 그러나 일부 기본값이 아닌 설정에서는 여전히 닿을 수 있었다(CVE-2021-45046, CVSS 9.0).
- 2.16.0: 메시지 lookup 기능을 없애고 JNDI를 기본으로 끔. 이후 lookup이 자기 자신을 되부르는 무한 재귀로 프로세스가 죽는 문제(CVE-2021-45105, 서비스 거부)가 나왔다.
- 2.17.0: 재귀 문제 수정. 마지막으로 공격자가 로깅 설정을 바꿀 수 있을 때의 JDBC Appender 문제(CVE-2021-44832)가 2.17.1에서 고쳐졌다.
Java 7·6 환경에는 2.12.x·2.3.x 줄에 따로 고친 판이 나왔다. 패치 전 임시 조치로는 서버의 바깥 방향 LDAP·RMI 연결을 막는 egress 제한이 권고됐다(AA21-356A).
배울 점
- 로그도 입력을 처리하는 코드다. 로그에 쓰는 값을 해석하는 기능은 그 자체로 공격 표면이다.
- 쓰는 줄도 몰랐던 부품이 가장 큰 구멍이 된다. 의존성 목록(SBOM)이 있어야 "우리가 영향받나"에 몇 분 안에 답한다.
- 첫 패치가 끝이 아니다. 큰 사건은 고친 판이 연달아 나오므로 공지를 계속 따라가야 한다.
- 서버가 바깥으로 아무 데나 접속할 수 없었다면 코드를 내려받는 단계에서 막혔다. egress 제한은 다음 사건에서도 통한다.
미국 사이버안전검토위원회(CSRB)는 2022년 7월 Log4j 사건 검토 보고서에서 취약한 Log4j가 앞으로도 오랫동안 시스템에 남을 것이라고 봤다.
Spring4Shell (2022, Spring Framework 데이터 바인딩)
무엇이 일어났나
2022년 3월 31일 Spring은 CVE-2022-22965를 공지했다. JDK 9 이상에서 돌아가는 Spring MVC·WebFlux 앱이 데이터 바인딩을 통해 원격 코드 실행에 노출될 수 있다는 내용이다. Log4Shell 넉 달 뒤라 "또 하나의 Log4Shell"이라는 뜻의 Spring4Shell이라는 별명이 붙었다. NVD 기준 CVSS 9.8이고, 2022년 4월 4일 KEV에 올랐다.
왜 그렇게 됐나
데이터 바인딩은 요청 매개변수를 자바 객체의 속성에 자동으로 채워 주는 기능이다. user.address.city 같은 이름은 점을 따라 속성의 속성까지 찾아간다. 개발자가 의도한 것은 폼 객체의 필드였지만, 모든 자바 객체에는 class라는 속성 경로가 붙어 있고 JDK 9에서 생긴 새 경로를 거치면 그 끝이 런타임 내부 객체까지 이어졌다. 바깥 요청이 어떤 속성을 바꿀지를 고를 수 있었다는 것이 핵심이다. Spring의 임시 조치가 바인딩에서 class로 시작하는 경로를 막는 것(disallowedFields)이었던 이유도 여기 있다.
영향 범위와 고친 판 — 전제 조건을 읽는 법
이 사건은 공지의 전제 조건을 꼼꼼히 읽는 연습이 된다. Spring의 첫 공지는 알려진 공격이 성립하는 조건을 이렇게 적었다.
- JDK 9 이상
- Apache Tomcat에 WAR로 배포(Spring Boot의 기본인 실행형 jar는 이 공격에 해당하지 않음)
spring-webmvc또는spring-webflux의존성- Spring Framework 5.3.0~5.3.17, 5.2.0~5.2.19 또는 그 이전
그래서 "우리는 jar로 배포하니 안전하다"고 결론 내리기 쉽지만, 같은 공지는 취약점의 성질이 더 일반적이라 다른 공격 경로가 있을 수 있다고 덧붙였다. 전제 조건은 "지금 알려진 공격"의 조건이지 "취약하지 않음"의 증명이 아니다. 고친 판은 Spring Framework 5.3.18·5.2.20이고, 이를 담은 Spring Boot 2.6.6·2.5.12가 함께 나왔다. 업그레이드가 어려운 곳에는 Tomcat 업그레이드, Java 8로 내리기, disallowedFields 설정이 임시 조치로 제시됐다.
배울 점
- 자동 바인딩은 편하지만 "바깥이 서버 객체의 어느 칸을 쓸 수 있나"를 정하는 일이다. 받을 필드를 허용 목록으로 정한 전용 객체(DTO)를 쓰면 이런 종류의 문제를 피한다.
- 런타임(JDK) 업그레이드가 오래된 코드에 새 길을 열 수 있다. 보안 검토는 코드만이 아니라 실행 환경 변화도 본다.
- "우리 구성은 해당 없음"이라는 판단에도 패치는 한다. 전제 조건은 시간이 지나며 넓어지기도 한다.
Confluence OGNL 인젝션 (2022)
무엇이 일어났나
2022년 6월 2일 Atlassian은 보안 공지에서 Confluence Server와 Data Center의 인증 없는 원격 코드 실행 취약점 CVE-2022-26134를 알리며, 이미 실제 공격이 일어나고 있다고 밝혔다. 고친 판이 나오기 전에 공지가 먼저 나온 경우다. CISA는 공지 당일 이 취약점을 KEV에 올렸다. 로그인하지 않은 누구나, 인터넷에 열린 사내 위키 서버에서 코드를 실행할 수 있었다.
왜 그렇게 됐나
OGNL(Object-Graph Navigation Language)은 자바 객체의 속성을 읽고 메서드를 부르는 표현식 언어다. 템플릿이나 설정에서 값을 꺼내 쓰라고 만든 것인데, 요청에 실린 바깥 입력이 이 평가기에 식으로 들어가 계산됐다. 식을 평가할 수 있으면 메서드를 부를 수 있고, 메서드를 부를 수 있으면 프로그램을 실행할 수 있다. 표현식 언어 인젝션은 MITRE가 CWE-917로 분류한다.
영향 범위와 고친 판
공지는 지원 중인 모든 판이 영향을 받는다고 했고, NVD 기록은 1.3.0 이후 판을 범위로 적는다. 고친 판은 7.4.17, 7.13.7, 7.14.3, 7.15.2, 7.16.4, 7.17.4, 7.18.1이다. 업그레이드 전 임시 조치로 특정 jar 파일을 바꿔 넣는 방법이 나왔고, Atlassian이 운영하는 클라우드판은 해당하지 않았다. NVD 기준 CVSS 9.8이다.
배울 점
- 표현식 평가기에는 바깥 입력을 넣지 않는다. 꼭 필요하면 쓸 수 있는 속성과 메서드를 좁힌 샌드박스 모드를 쓴다.
- 인터넷에 그대로 열린 사내 도구는 공격자에게 가장 먼저 발견된다. 사내용은 VPN·접근 프록시 뒤에 둔다(제로 트러스트 노트).
- 패치가 없는 날에도 할 일은 있다. 인터넷 노출 차단, 임시 조치, 침해 흔적 찾기를 먼저 한다.
자바 역직렬화의 뿌리 (2015, Commons Collections)
무엇이 일어났나
2015년 1월 28일 Gabriel Lawrence와 Chris Frohoff가 AppSec California에서, 자바 직렬화 데이터를 받는 서버가 흔한 라이브러리 Apache Commons Collections를 갖고만 있어도 원격 코드 실행에 노출된다는 것을 보였다. 그해 11월 6일에는 WebLogic, WebSphere, JBoss, Jenkins 같은 제품별 공격 시나리오가 공개돼 큰 소동이 났다. Commons Collections 보안 페이지에 따르면 라이브러리 자체에는 CVE가 붙지 않았고, 제품마다 CVE가 붙었다(예: WebLogic의 CVE-2015-4852).
왜 그렇게 됐나
역직렬화(deserialization)는 바이트 흐름에서 객체를 되살리는 일이다. 자바의 기본 직렬화는 바이트 안에 적힌 클래스 이름대로 객체를 만들고, 되살리는 도중에 그 클래스의 메서드를 부른다. 공격자는 새 코드를 보낼 필요가 없다. 서버의 클래스 경로에 이미 있는 클래스들을 골라, 하나가 되살아나며 부른 메서드가 다음 객체의 메서드를 부르도록 엮는다. 이 사슬을 가젯 체인(gadget chain)이라 부른다. Commons Collections에는 직렬화 가능하면서 리플렉션으로 임의 메서드를 부르는 클래스가 있어 사슬의 좋은 고리가 됐다.
영향 범위와 고친 판
Commons Collections 3.2.2는 위험한 클래스의 역직렬화를 기본으로 거부하게 했고, 4.1은 그 클래스들의 직렬화 지원을 아예 없앴다(영향 판 3.0~4.0). 하지만 Apache Commons 팀은 당시 성명에서 이 라이브러리만 고친다고 앱이 안전해지지 않는다고 못 박았다. 다른 라이브러리에도 가젯이 있기 때문이다. 문제는 특정 클래스가 아니라 믿을 수 없는 데이터를 역직렬화하는 설계였다.
배울 점
- 믿을 수 없는 쪽에서 온 데이터는 언어 고유 직렬화(Java 직렬화, Python
pickle등)로 되살리지 않는다. JSON처럼 데이터만 담는 형식을 쓰고, 받은 뒤 스키마로 검증한다(CWE-502). - 꼭 써야 하면 되살리기 전에 클래스 이름을 확인하는 look-ahead 검사와 허용 목록을 둔다. 자바는 JEP 290으로 이를 위한 직렬화 필터를 표준에 넣었다.
- 차단 목록은 새 가젯이 나올 때마다 뚫린다. 허용 목록이 기본이다. 자세한 언어별 지침은 OWASP 역직렬화 치트 시트에 있다.
AI 모델 파일에서 같은 문제가 다시 나타난 이야기는 AI 보안 노트의 pickle 절에 있다. 10년 뒤 JavaScript 쪽에서 되풀이된 사례는 아래 React Server Components 절에서 본다.
PHP-CGI 인자 주입 (2024, CVE-2024-4577)
무엇이 일어났나
2024년 6월 6일 PHP 8.3.8·8.2.20·8.1.29가 나오며 CVE-2024-4577을 고쳤다. Windows에서 Apache와 PHP-CGI를 함께 쓰는 서버에서, 바깥 요청이 PHP 실행 파일에 명령줄 옵션을 넘겨 소스 코드를 드러내거나 임의 PHP 코드를 실행하게 할 수 있었다. NVD 기준 CVSS 9.8이고, 2024년 6월 12일 KEV에 올랐다. PHP의 보안 공지 제목은 "CVE-2012-1823의 우회"다.
왜 그렇게 됐나
뿌리는 2012년의 CVE-2012-1823이다. CGI 방식에서는 웹 서버가 요청의 쿼리 문자열 일부를 php-cgi의 명령줄 인자로 넘기는 경우가 있어, 하이픈(-)으로 시작하는 값이 PHP 옵션으로 읽혔다. 당시 수정은 "하이픈으로 시작하면 옵션으로 보지 않는다"는 검사였다.
2024년의 우회는 문자 인코딩에서 나왔다. Windows는 유니코드 문자를 옛 코드 페이지로 바꿀 때 딱 맞는 글자가 없으면 비슷한 글자로 대신하는 Best-Fit 변환을 한다. 일본어(932)·간체 중국어(936)·번체 중국어(950) 코드 페이지에서는 소프트 하이픈 같은 글자가 보통 하이픈으로 바뀌었다. PHP의 검사는 변환 전 글자를 보고 통과시켰는데, 실제로 실행되는 명령줄은 변환 후 글자를 받았다. 검사한 값과 쓰인 값이 달랐다.
영향 범위와 고친 판
영향은 좁지만 깊었다. Windows용 PHP만, PHP를 CGI 방식으로 돌리는 구성만, 그리고 시스템 로캘이 일본·중국·홍콩·마카오·싱가포르·대만 쪽인 서버가 대상이었다. 다만 PHP 공지에 실린 제보자의 설명에 따르면 개발용으로 널리 쓰는 XAMPP의 Windows 기본 설치가 그대로 해당했다. 고친 판은 8.3.8·8.2.20·8.1.29이고, 이미 지원이 끝난 8.0·7·5 줄에는 패치가 나오지 않았다. 몇 달 뒤에는 비표준 코드 페이지 설정에서 이 수정마저 우회되는 CVE-2024-8926이 나와 8.3.12·8.2.24·8.1.30에서 다시 고쳐졌다.
배울 점
- 검사는 실제로 쓰이는 형태에서 한다. 인코딩 변환·정규화·디코딩이 검사 뒤에 일어나면 검사는 의미를 잃는다.
- 입력을 다른 프로그램의 명령줄로 넘기는 구조(CWE-88, 인자 주입) 자체를 피한다. PHP라면 CGI 대신 FastCGI(PHP-FPM)나 모듈 방식을 쓴다.
- "개발용 패키지"가 인터넷에 열려 운영되는 경우를 늘 의심한다. 지원이 끝난 판은 고쳐지지 않는다.
React Server Components·Next.js RCE (2025)
무엇이 일어났나
2025년 12월 3일 React 팀은 공지에서 React Server Components(RSC)의 인증 없는 원격 코드 실행 취약점 CVE-2025-55182를 알렸다. CVSS 10.0이고, CISA는 이틀 뒤인 12월 5일 KEV에 올렸다. 같은 날 Next.js도 자기 쪽 영향을 CVE-2025-66478로 공지했는데, NVD는 이 번호를 CVE-2025-55182의 중복으로 거부 처리했다. 같은 결함을 두 번호로 부른 셈이다.
왜 그렇게 됐나
RSC에서는 브라우저가 서버 쪽 함수(Server Function)를 부를 때, 인자를 React 고유 형식으로 직렬화해 요청 본문에 실어 보낸다. 서버는 이 본문을 객체로 되살린 뒤 함수를 부른다. React 공지는 취약한 코드가 Server Function 엔드포인트로 온 HTTP 요청의 내용을 안전하지 않게 역직렬화했다고 설명한다. 2015년 자바 사건과 같은 갈래가 10년 뒤 JavaScript 프레임워크에서 되풀이된 것이다. 두 공지 모두 아직 패치하지 못한 곳을 위해 기술적 세부 사항을 일부러 줄였다.
브라우저가 서버 쪽 함수(Server Function)를 부를 때, 인자를 React 고유 형식으로 직렬화해 요청 본문에 실어 보낸다. 이 요청은 누구나 직접 만들어 보낼 수 있다.
영향 범위와 고친 판
- React:
react-server-dom-webpack·react-server-dom-parcel·react-server-dom-turbopack의 19.0.0, 19.1.0, 19.1.1, 19.2.0. 고친 판은 19.0.1, 19.1.2, 19.2.1이다. - Next.js: App Router를 쓰는 15.x, 16.x, 그리고 14.3.0-canary.77 이후 canary. 처음 고친 판은 15.0.5, 15.1.9, 15.2.6, 15.3.6, 15.4.8, 15.5.7, 16.0.7이다. 13.x, 14.x 안정판, Pages Router, Edge Runtime은 해당하지 않았다.
- React Router의 RSC 기능, Waku, Parcel·Vite의 RSC 플러그인, Redwood SDK 같은 다른 RSC 구현도 영향 목록에 올랐다.
눈여겨볼 점은 두 가지다. 첫째, React 공지는 Server Function을 직접 만들지 않았더라도 RSC를 지원하는 앱이면 취약할 수 있다고 했다. 개발자가 따로 무언가를 켜지 않은 기본 구성이 대상이었다. 둘째, 후속 문제가 이어졌다. 12월 11일 후속 공지가 서비스 거부와 소스 코드 노출 취약점을 알렸고, 그래서 위의 처음 고친 판보다 각 판 줄의 최신 패치로 올려야 한다. Next.js는 12월 4일 오후(태평양 시간)까지 패치 없이 온라인이던 앱에 쓰던 비밀 값(API 키 등)을 모두 바꾸라고 권했다.
배울 점
- 프레임워크가 숨겨 둔 통신 형식도 역직렬화다. "우리는 eval을 안 쓴다"가 안전의 증명이 되지 않는다.
- 코드 실행 취약점은 패치로 끝나지 않는다. 노출 기간 동안 비밀 값이 새었다고 보고 교체한다(침해 가정).
- 호스팅 업체의 임시 차단 규칙은 시간을 벌어 줄 뿐이다. React 공지도 이것을 업그레이드 대신 믿지 말라고 했다.
비교: Next.js 미들웨어 인가 우회 (2025, CVE-2025-29927)
무엇이 일어났나
같은 해 3월 21일 공개된 CVE-2025-29927은 원격 코드 실행이 아니라 인가 우회다. 그래도 RCE와 나란히 볼 만하다. Next.js에서 로그인 확인 같은 인가 검사를 미들웨어에만 두었다면, 요청에 특정 내부용 헤더를 붙이는 것만으로 그 검사를 건너뛸 수 있었다. GitHub이 매긴 CVSS는 9.1이다.
왜 그렇게 됐나
Vercel의 사후 분석에 따르면, 미들웨어가 안에서 다시 앱을 부를 때 미들웨어가 끝없이 되불리는 것을 막으려고 Next.js는 x-middleware-subrequest라는 문서화되지 않은 내부 헤더를 썼다. 이 헤더가 있으면 "미들웨어는 이미 돌았다"고 믿었는데, 그 헤더가 바깥 요청에서 왔는지는 확인하지 않았다. 안쪽끼리만 쓰려던 신호를 바깥도 보낼 수 있었다.
영향 범위와 고친 판
영향 판은 11.1.4부터 12.3.5·13.5.9·14.2.25·15.2.3 직전까지이고, 고친 판은 12.3.5, 13.5.9, 14.2.25, 15.2.3이다. 사후 분석은 next start나 standalone 출력으로 직접 호스팅하며 미들웨어를 쓰는 앱이 영향을 받았고, Vercel·Netlify·Cloudflare Workers 호스팅과 정적 내보내기는 해당하지 않았다고 정리한다. 패치가 어려우면 바깥 요청의 그 헤더를 앞단에서 지우라는 것이 공지의 임시 조치였다.
배울 점
- "내부에서만 쓰는 헤더"는 없다. 바깥과 같은 통로로 오는 신호는 신뢰 경계에서 지우거나 서명해 확인한다.
- 인가는 한 겹에 몰지 않는다. 미들웨어는 1차 거름망이고, 데이터에 접근하는 서버 코드에서 다시 확인한다(인증·인가 노트).
- RCE가 아니어도 점수가 높다. CVSS는 "무엇을 할 수 있나"를 재므로, 인증 없이 보호된 데이터에 닿으면 치명적이다.
대응의 공통 틀
사례들을 겹쳐 보면 대응 순서가 거의 같다. 한 겹이 뚫려도 다음 겹이 피해를 줄이도록 다섯 겹으로 생각한다.
1. 찾기: 우리가 그 부품을 쓰는가
Log4Shell 때 가장 오래 걸린 일은 "어디에 Log4j가 있나"를 알아내는 것이었다. 직접 의존성뿐 아니라 전이 의존성, 사서 쓰는 제품 안의 부품까지 포함한 SBOM(소프트웨어 자재 명세서)이 있으면 공지가 뜬 날 바로 영향 여부를 판단한다. SBOM과 공급망 보안은 시스템·소프트웨어 보안 노트에 정리해 두었다.
2. 고치기: 패치 속도와 KEV
공개에서 실제 악용까지는 며칠이면 충분하다. 아래 그림은 이 글의 사례가 공개된 뒤 CISA의 알려진 악용 취약점(KEV) 목록에 오르기까지 걸린 날수다. KEV는 "실제로 공격에 쓰이고 있다"는 증거가 확인된 취약점만 올리므로, CVSS 점수와 함께 패치 순서를 정하는 가장 실용적인 기준이 된다. CISA는 2026년 6월 BOD 26-04로 연방 기관에 KEV 목록과 자산 노출 정도에 따라 패치 기한을 정하게 했다.
패치 주기, CVE·CVSS 읽는 법, 아직 KEV에 오르지 않은 취약점의 악용 가능성을 확률로 추정하는 EPSS는 사람과 운영 노트에 있다.
3. 버티기: WAF와 설정 임시 조치
WAF(웹 애플리케이션 방화벽) 규칙은 패치까지 시간을 버는 수단이다. Log4Shell에서는 같은 뜻의 문자열을 여러 겹으로 감추는 우회가 쏟아졌고, CISA 권고도 탐지 방법 하나에 기대지 말라고 했다. Spring의 disallowedFields, Confluence의 jar 교체, Next.js의 헤더 지우기도 모두 같은 자리다. 임시 조치를 했다면 그 사실과 되돌릴 날짜를 기록하고, 패치 후 걷어 낸다.
4. 줄이기: 뚫려도 덜 아프게
- 최소 권한: 서버 프로세스를 관리자 권한으로 돌리지 않는다. 코드 실행의 피해는 그 프로세스의 권한만큼이다.
- egress 제한: 앱 서버가 바깥 아무 곳에나 접속하지 못하게 한다. Log4Shell은 코드를 받아 오는 단계에서, 많은 공격은 훔친 데이터를 내보내는 단계에서 막힌다.
- 샌드박스와 격리: 컨테이너·읽기 전용 파일 시스템·샌드박스로 실행 범위를 좁히고, 마이크로세그멘테이션으로 옆 시스템으로 번지는 길을 끊는다.
- 비밀 값 분리: 오래가는 키를 환경 변수에 두기보다 짧게 사는 자격 증명을 쓰면, React 사례처럼 "모든 비밀을 바꾸라"는 일이 작아진다.
5. 알아채기: 탐지와 사고 대응
패치를 했어도 패치 전에 이미 뚫렸을 수 있다. CISA의 Log4j 권고는 취약했던 자산을 침해된 것으로 가정하고 격리한 뒤, 공개 전후의 설정 변경과 침해 흔적을 찾으라고 했다. 낯선 바깥 연결, 웹 서버 프로세스가 띄운 셸, 새로 생긴 계정과 예약 작업이 대표적인 흔적이다. 찾았다면 사고 대응 절차로 넘어간다(사람과 운영 노트의 사고 대응 절).
마지막으로 처음 그림으로 돌아가자. 새 기능을 설계할 때 "이 입력은 결국 무엇에 해석되나"를 묻는 습관이 가장 싼 대응이다. 역직렬화 대신 데이터 형식, 표현식 평가 대신 정해진 값, 동적 로딩 대신 허용 목록, 명령줄 대신 API를 고르면 이 글의 사례 대부분이 처음부터 생기지 않는다.
- CVE-2021-44228 (Log4Shell) — NVD
- CVE-2021-45046 — NVD
- Log4j Security Vulnerabilities — Apache Logging Services
- AA21-356A: Mitigating Log4Shell and Other Log4j-Related Vulnerabilities — CISA
- CSRB Review of the December 2021 Log4j Event — CISA
- CVE-2022-22965: Spring Framework RCE via Data Binding on JDK 9+ — Spring
- Spring Framework RCE, Early Announcement — Spring
- Confluence Security Advisory 2022-06-02 — Atlassian
- CVE-2022-26134 — NVD
- CWE-917: Expression Language Injection — MITRE
- Commons Collections Security Reports — Apache Commons
- Apache Commons statement to widespread Java object de-serialisation vulnerability — Apache Software Foundation
- CVE-2015-4852 — NVD
- CWE-502: Deserialization of Untrusted Data — MITRE
- JEP 290: Filter Incoming Serialization Data — OpenJDK
- Deserialization Cheat Sheet — OWASP
- CVE-2024-4577 — NVD
- GHSA-3qgc-jrrr-25jv: Bypass of CVE-2012-1823, Argument Injection in PHP-CGI — PHP
- CVE-2012-1823 — NVD
- CVE-2024-8926 — NVD
- CWE-88: Argument Injection — MITRE
- Critical Security Vulnerability in React Server Components — React
- Denial of Service and Source Code Exposure in React Server Components — React
- CVE-2025-55182 — NVD
- Security Advisory: CVE-2025-66478 — Next.js
- GHSA-f82v-jwr5-mffw: Authorization Bypass in Next.js Middleware — Next.js
- Postmortem on Next.js Middleware bypass — Vercel
- BOD 26-04: Prioritizing Security Updates Based on Risk — CISA