Node.js Brotli RCE 취약점, 내 버전은 안전할까? 2분 만에 확인하기

CVE-2026-111497은 현재 실제로 악용되고 있는 Node.js Brotli 디코더 취약점입니다. 2분 만에 사용 중인 버전을 확인하고, 어떤 프레임워크와 HTTP 클라이언트가 해당 디코더를 호출하는지 파악한 뒤 설치 방식에 맞춰 패치하세요. 당장 업그레이드할 수 없다면 완화 조치를 적용하시기 바랍니다.

Ashley Innocent

Ashley Innocent

5 November 2026

Node.js Brotli RCE 취약점, 내 버전은 안전할까? 2분 만에 확인하기

Apidog 엔터프라이즈

온프레미스 배포

SSO & RBAC

SOC 2 준수

Apidog Enterprise 살펴보기

지금 즉시 버전을 확인하세요. 현재 사용 중인 버전이 아래 표의 기준치 미만이라면, 공격자들이 이미 악용하고 있는 Node.js Brotli 원격 코드 실행(RCE) 취약점(CVE-2026-111497)의 영향을 받습니다.

node -v
릴리스 라인 취약 버전 패치 적용 버전 최신 버전 (2026년 11월 5일 기준)
26.x 26.12.1 미만 26.12.1 26.13.0
24.x 24.22.1 미만 24.22.1 24.22.2
22.x 22.23.4 미만 22.23.4 22.23.4
25.x, 20.x 및 이전 버전 지원 종료(EOL): 패치 미제공 및 취약점 미평가 패치 없음 24 또는 26 버전으로 마이그레이션 권장

패치 적용 여부를 확실히 확인하려면 바이너리에 직접 상수를 조회해 보세요.

node -p "require('node:zlib').constants.BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH"

이 상수는 이번 보안 패치에서 추가되었습니다. 현재 지원되는 3개 라인의 공식 macOS 빌드 9개를 대상으로 테스트한 결과, 패치가 적용된 빌드는 모두 -17을 출력했고 이전 빌드는 모두 undefined를 출력했습니다. 단, 배포판(Linux distro)에 따라 패치만 백포트하고 상수는 추가하지 않았을 수도 있으므로 node -v 출력을 1차 기준으로 삼으시기 바랍니다.

process.versions.brotli 값은 확인하지 마세요. 24.21.0과 24.22.1 모두 1.2.0을 출력하며, 22.23.3과 22.23.4 모두 1.1.0을 출력합니다. Node.js가 보고되는 버전 번호를 변경하지 않고 수정 사항만 체리픽했기 때문에 이 필드로는 패치 여부를 판별할 수 없습니다.

핵심 요약 (TL;DR)

질문 내용
어떤 취약점인가요? Node.js에 내장된 Brotli 디코더의 버퍼 오버플로(Out-of-bounds write) 취약점 (Node2Shell 또는 HTTP2Shell로 불림)
영향 프로세스 비정상 종료(크래시), 메모리 정보 유출, 원격 코드 실행(RCE)
심각도 Node.js 기준 HIGH, NVD 기준 9.8 CRITICAL (CVSS 3.1)
실제 악용 사례가 있나요? 예. 2026년 10월 27일 CISA KEV(알려진 악용 취약점 카탈로그)에 등재되었습니다.
로그인이 필요한가요? 아니요. NVD 벡터 및 Akamai 분석에 따르면 인증 없이 공격 가능합니다.
공격 노출 대상은? 외부에서 들어오는 제어 불가능한 Brotli 데이터를 디코딩하는 애플리케이션: Express 5 요청 본문, fetch() 응답, 직접 호출하는 node:zlib 등
해결 방법 Node.js 런타임 업그레이드, 컨테이너 이미지 재빌드, 실행 프로세스 재시작
임시 완화책이 있나요? 공식 권고문에는 없습니다. 아래 설명된 임시 조치를 통해 노출 면을 줄일 수 있지만 완전한 해결책은 아닙니다.
Timeline of CVE-2026-111497: Node.js fixes on October 21, CVE published October 22, exploit traffic reported October 26, CISA KEV listing October 27, KEV due date November 10, next Node.js security release November 11

ELLIO는 보안 패치 발표 후 불과 5일 만인 10월 26일 자사 센서에서 공격 트래픽을 감지했다고 발표했습니다. 캡처된 페이로드는 x86-64 아키텍처의 Node.js 24.22.0 버전을 타깃으로 하고 있었습니다. 해당 페이로드의 최종 단계는 ELLIO의 테스트 환경에서 실패했으나, ELLIO는 기본 24.22.0 빌드에서 코드 실행이 실제로 가능함을 확인했습니다. 미국 CISA는 연방 기관에 11월 10일까지 조치를 완료하도록 지시했으며 랜섬웨어 악용 여부는 "Unknown(알 수 없음)"으로 분류했습니다. Akamai의 원격 측정 데이터에 따르면 10월 26일 기준으로 전체 네트워크의 61%가 취약한 버전을 실행 중이었습니다.

Node2Shell 취약점 상세 분석: Brotli 버그의 동작 원리

Node2Shell은 Node.js 런타임 내부에 번들로 제공되는 Brotli 디코더에서 발생하는 버퍼 오버플로(Out-of-bounds write) 결함입니다. 조작된 압축 스트림을 처리할 때 디코더가 8바이트 내부 버퍼의 끝을 넘어 데이터를 쓰게 되며, 이로 인해 프로세스 메모리가 손상됩니다. 그 결과 애플리케이션 충돌, 메모리 유출, 혹은 서버 내 임의 코드 실행으로 이어질 수 있습니다.

근본적인 원인은 오류 분류 오류(misclassified error)에 있습니다. 유효하지 않은 블록 전환(block switch)이 발생했을 때 "데이터 손상" 대신 "입력 데이터 필요"라는 오류 코드가 잘못 반환되어, 스트리밍 디코더가 해당 작은 버퍼에 바이트를 계속 덧붙여 쓰게 된 것입니다.

이 버그는 Google Brotli 라이브러리 1.0.3부터 1.2.0 버전에 존재하므로 Node.js에만 국한된 문제는 아닙니다. Node.js 팀은 업스트림 커밋 0ed4268a를 24.22.1 릴리스 및 관련 라인에 체리픽하여 문제를 해결했습니다. 이 취약점은 OpenAI Codex Security의 보안 연구원 Khai Tran과 Sylvie Mayer에 의해 보고되었습니다.

디코더가 패키지가 아니라 런타임 자체에 내장되어 있기 때문에 일반적인 의존성 검사 도구(SCA)로는 감지할 수 없습니다. 실제로 패치가 적용되지 않은 Node.js 24.15.0 환경에서 Express 5 테스트 프로젝트를 대상으로 npm install을 실행해도 취약점이 0건으로 보고되었습니다.

실제 취약점에 노출되는 환경

취약한 런타임을 사용하고 있다는 사실은 위험 조건의 절반에 불과합니다. 나머지 절반은 신뢰할 수 없는 Brotli 데이터를 디코더로 전달하는 코드 경로가 존재하는지 여부입니다. Node.js 24.22.1 환경에서 Content-Encoding: br 헤더와 함께 악의적이지 않은 유효하지 않은 본문(not-valid-brotli 텍스트)을 전송하고 각 클라이언트에 정상적인 Brotli 데이터를 제공하며 경로별 동작을 테스트했습니다.

호출 경로 테스트 버전 디코더 실행 여부 테스트 결과
Express 5 express.json() 5.3.0 (body-parser 2.4.0) 예 (기본값) 400, “Decompression failed”
Express 4 express.json() 4.22.3 (body-parser 1.20.9) 아니요 415, 지원하지 않는 인코딩(unsupported encoding)
Koa @koa/bodyparser 6.1.0 예 (기본값) 500, 로그에 BrotliDecoder 오류 발생
Fastify (@fastify/compress 사용) 9.3.0 예 (등록 시) 400, FST_CP_ERR_INVALID_CONTENT
기본 Fastify 5.12.8 아니요 400, 올바르지 않은 JSON(invalid JSON)
순수 node:http 서버 또는 http.get 24.22.1 아니요 원시 바이트(raw bytes) 그대로 통과
내장 fetch() 24.22.1 예 (항상) 응답 본문 디코딩 수행
axios, got, node-fetch 1.21.0, 16.2.0, 3.3.2 예 (기본값) 응답 본문 디코딩 수행
zlib.brotliDecompress*, createBrotliDecompress 모든 버전 예 Brotli 디코더 API 자체 호출
Diagram of paths into the Brotli decoder in node:zlib: Express 5 parsers, Koa body parsers and Fastify with @fastify/compress on the inbound side, fetch, axios and got on the outbound side, while Express 4, plain Fastify and bare node:http do not reach it

두 가지 결과는 특히 주목해야 합니다. 첫째, fetch()는 요청 시 Accept-Encoding: identity를 명시적으로 보내더라도 응답 헤더를 그대로 따르기 때문에 Brotli 디코딩을 수행합니다. 즉, 애플리케이션이 호출하는 외부 서버가 디코더에 영향을 줄 수 있으므로 웹훅 테스터, 링크 미리보기 생성기, URL 수집기(importer) 등은 취약점에 직접 노출됩니다. 둘째, 단순히 400 응답을 반환한다고 안전한 것은 아닙니다. Express 5와 기본 Fastify 모두 400을 반환하지만, 디코더를 실행한 것은 Express 5뿐입니다. 에러 메시지의 구체적인 내용을 반드시 확인해야 합니다.

다음 세 가지 명령어를 사용해 프로젝트 내 노출 경로를 확인하세요.

# Direct decoder use
grep -rnE "brotliDecompress|createBrotliDecompress" src/

# Parsers that decode request bodies
npm ls express body-parser @koa/bodyparser @fastify/compress

# Base images and pinned versions
grep -rnE "^FROM .*node" --include='Dockerfile*' .

새로운 fetch() 호출 하나만으로도 이러한 판별 결과가 뒤바뀔 수 있으므로, 현재 "노출되지 않음" 상태라 하더라도 반드시 패치를 적용해야 합니다.

CVE-2026-111497 패치 적용 방법

단순히 최소 패치 버전에 맞추지 말고 사용 중인 라인의 최신 빌드로 업그레이드하세요. 11월 5일 기준으로 다음 소스 모두 패치된 빌드를 제공하고 있습니다.

설치 환경 조치 방법
nvm nvm install 24 && nvm alias default 24
Homebrew brew upgrade node (또는 node@24, node@22)
Docker FROM node:24.22.2-slim 지정 후 docker build --pull 실행
Linux 배포판 패키지 이전 버전 번호 유지 상태로 패치만 백포트되었을 수 있으므로 배포판별 보안 권고안 확인
CI/CD 환경 .nvmrc, engines, node-version 설정을 갱신하고 재빌드 수행
서버리스(Serverless) 배포된 함수에서 process.version을 출력해 직접 검증 (플랫폼이 자동으로 업데이트했다고 단정하지 말 것)

그런 다음 실무에서 자주 간과되는 두 가지 사각지대를 조치해야 합니다.

마지막으로 본 글의 서두에서 안내한 검증 스크립트를 배포된 컨테이너 내부, CI 러너, 개발자 로컬 머신에서 실행하세요. 라인별 최신 보안 빌드 목록은 다음 명령어로 확인할 수 있습니다.

curl -s https://nodejs.org/dist/index.json | jq -r '[.[] | select(.security == true and (.version | test("^v(22|24|26)\\.")))] | group_by(.version | split(".")[0]) | .[] | max_by(.date) | "\(.version)  \(.date)"'

아직 Node.js 20 또는 25 버전을 사용 중이신가요? 해당 버전에는 패치 빌드가 제공되지 않습니다. Node.js 20 수명 주기 종료(EOL) 안내를 확인하고 상위 버전으로 즉시 마이그레이션하세요.

즉시 패치할 수 없는 경우의 임시 완화책

다음 조치들은 시간을 벌어줄 뿐이며 정식 업그레이드를 완전히 대체할 수는 없습니다.

  1. 에지(Edge) 단에서 br 요청 차단: 리버스 프록시나 로드 밸런서에서 대소문자 구분 없이, 그리고 gzip, br 형태의 다중 인코딩 목록을 포함하여 Content-Encoding: br 헤더를 차단합니다.
  2. 애플리케이션 레벨에서 차단: 본문 파서(body parser)보다 앞선 위치에 아래 미들웨어를 배치합니다. Express 5 테스트 환경에서 415 상태 코드를 성공적으로 반환했습니다.
app.use((req, res, next) => {
  const encoding = req.headers['content-encoding'] || '';
  if (/\bbr\b/i.test(encoding)) return res.status(415).end();
  next();
});
  1. 압축 해제 기능 비활성화: express.json({ inflate: false }) 옵션을 사용하면 동일한 요청에 대해 415 오류를 반환합니다. 단, gzip 본문 처리도 함께 거부되므로 클라이언트 영향도를 먼저 검토해야 합니다.
  2. 아웃바운드 응답 제어: axios는 decompress: false 옵션을 지원하며, 테스트 결과 바이트가 압축 해제되지 않고 유지되었습니다. 반면 fetch()는 해당 동작을 끄는 표준 옵션이 없으므로 허용 목록(allowlist) 및 아웃바운드 이그레스(egress) 규칙을 적용해 접근 가능한 호스트를 제한해야 합니다.
  3. 피해 반경(Blast Radius) 격리: Node.js 서비스를 망 분리하고 코어 덤프(core dump) 파일 접근 권한을 제한하세요. ELLIO에 따르면 프로세스 비정상 종료 시 코어 덤프에 메모리 내부 데이터가 노출될 수 있습니다.

ELLIO가 초기에 포착했던 징후를 주시하세요. 호스트가 Brotli를 디코딩하는지 탐색하기 위해 Brotli로 인코딩된 요청 본문이 급증하는 현상이 발생할 수 있습니다. JSON API로 유입되는 비정상적인 Content-Encoding: br 트래픽이나 워커 프로세스의 갑작스러운 충돌 증가는 즉각적인 조사 대상입니다.

업그레이드 후 API 안정성 검증: AI 에이전트로 테스트 자동화하기

런타임의 메이저/마이너 버전이 바뀌면 TLS, DNS, HTTP 동작 방식이 변경될 수 있으며, 실제로 10월 릴리스에서는 세 영역 모두에 변경이 있었습니다. 이를 수작업으로 일일이 검증하지 마세요. 코딩 에이전트를 통해 업그레이드 전후로 Apidog CLI를 실행하고 변경 사항을 자동으로 리포트하도록 구성할 수 있습니다.

npm install -g apidog-cli@latest
apidog login --with-token <YOUR_TOKEN>
npx -y skills add https://apidog.com   # pick your agent when prompted

에이전트에게 다음 프롬프트를 전달합니다.

Run Apidog test suite <id> in project <id> against staging and save the
results to reports/before. After I upgrade Node.js, add a test case for
POST /orders that sends the plain text "not-valid-brotli" with
Content-Encoding: br, asserts a 4xx status, then checks GET /health
returns 200. Rerun the suite to reports/after and list every request
whose result changed.

에이전트는 내부적으로 다음 명령어들을 순차적으로 실행합니다.

apidog run --test-suite <id> --project <id> -e <envId> -r cli,junit --out-dir reports/before
apidog cli-schema get test-case-create
apidog cli-schema validate test-case-create --file case.json
apidog test-case create --project <id> --file case.json
apidog run --test-case <caseId> --project <id> -e <envId> -r cli,json
apidog run --test-suite <id> --project <id> -e <envId> -r cli,junit --out-dir reports/after

추가된 테스트 케이스는 익스플로잇 코드가 아니라 정상적인 헬스 체크 목적입니다. 본문은 단순 텍스트이며, 테스트 결과 취약한 빌드와 수정된 빌드 모두 동일한 패딩 오류로 실패했습니다. 그러나 이 테스트는 실질적인 문제점을 발견해 줍니다. Koa 테스트 환경에서는 디코드 오류가 제대로 처리되지 않아 500 응답이 반환되었으나, 공개 API는 정상적으로 4xx 상태 코드를 반환해야 합니다. 이 테스트는 반드시 스테이징 환경에서만 실행하세요.

모든 명령어 옵션은 Apidog 실행 명령어 레퍼런스에서 확인할 수 있으며, CI/CD 가이드를 참고해 동일한 JUnit 기반 테스트를 파이프라인에 통합할 수 있습니다. Apidog의 무료 플랜은 최대 4명의 사용자까지 지원합니다.

다가오는 11월 11일 보안 릴리스에 주목해야 하는 이유

Node.js 프로젝트 팀은 2026년 11월 11일경 26.x, 24.x, 22.x 버전을 대상으로 최고 심각도 HIGH 수준의 추가 보안 릴리스를 사전 공지했습니다. 아직 공개된 CVE는 없습니다. 사전 준비 방법은 11월 릴리스 대비 가이드를 참고하시기 바랍니다.

다음 릴리스 일정까지 미루지 말고 이번 Brotli 취약점을 지금 즉시 패치해야 합니다. 해당 버그는 이미 세부 정보가 대중에 공개되었고 실제 공격 트래픽이 발생하고 있습니다. 또한 이번 주에 업그레이드 프로세스를 진행해 두면 자산 목록 식별, 이미지 재빌드, 테스트 스위트 실행 등 다음번 보안 패치를 위한 완벽한 사전 리허설이 됩니다. 과거 PHP2Shell과 React2Shell 역시 동일한 양상으로 전개되었습니다.

자주 묻는 질문 (FAQ)

CVE-2026-111497이 실제로 악용되고 있나요? 예. CISA는 2026년 10월 27일 Google Brotli 항목으로 이 취약점을 알려진 악용 취약점 카탈로그(KEV)에 등록했으며, ELLIO는 10월 26일에 이미 공격 트래픽을 관측했습니다.

심각도가 HIGH인가요, CRITICAL인가요? 두 분류 모두 맞습니다. Node.js 팀 자체 평가는 HIGH이며, NVD의 CVSS 3.1 기준 점수는 9.8 CRITICAL, Google에서 발행한 CVSS 4.0 점수는 9.2입니다. 실제 공격이 진행 중이라는 사실에 초점을 맞춰 대응 계획을 수립해야 합니다.

Express 4를 사용 중인데 안전한가요? 요청 본문 파싱 과정에서는 디코더로 연결되지 않지만, 애플리케이션 내의 fetch(), axios 호출이나 직접적인 node:zlib 사용 시에는 여전히 취약점에 노출됩니다. 아울러 해당 보안 릴리스에는 다른 9개의 CVE 패치도 포함되어 있으므로 반드시 업그레이드해야 합니다.

Node.js 20 버전도 영향을 받나요? 공식 프로젝트에서는 지원이 종료된(End-of-Life) 라인에 대해 취약성 평가를 진행하지 않으며 패치도 제공하지 않습니다. 20.x 및 25.x 버전은 패치되지 않은 잠재적 취약 버전으로 간주하고 상위 버전으로 마이그레이션해야 합니다.

Node.js 외의 다른 환경에도 영향을 미치나요? 예. GitHub 보안 권고문에 따르면 PyPI의 brotli 및 brotlicffi 패키지도 포함되며, Bun 1.4.4 및 .NET 등에서도 벤더 패치가 발표되었습니다. Node.js 업그레이드는 자체 내장 디코더만 수정하므로 각 디코더 라이브러리를 개별적으로 업데이트해야 합니다.

Apidog에서 API 설계-첫 번째 연습

API를 더 쉽게 구축하고 사용하는 방법을 발견하세요