2026-11-04 21:30
일주일 전 Node.js 서버를 부랴부랴 패치하면서 “이제 끝났다” 하셨나요? 아쉽지만 이번 사태는 Node.js만의 이야기가 아니었습니다. 결함의 뿌리는 구글이 만든 공통 압축 라이브러리 Brotli였고, 공개 2주 차에 접어들면서 Brotli 취약점은 파이썬·닷넷·리눅스 배포판·방화벽·게임 엔진까지 공지가 줄줄이 이어지고 있습니다. 이 글은 지난번 Node.js 취약점 Node2Shell 총정리의 후속편으로, Node.js 밖에서 무엇을 어떤 버전으로 올려야 하는지에 집중했습니다.
이번 결함의 공식 번호는 CVE-2026-111497입니다. Node.js용 번호가 따로 있는 것이 아니라, 구글이 Brotli 라이브러리에 붙인 원천 CVE 하나를 Node.js·파이썬·닷넷이 함께 쓰고 있습니다. 해외에서는 Node2Shell, Brotli2Shell, HTTP2Shell 같은 별칭이 섞여 쓰여 검색할 때 헷갈리기 쉬운데, 모두 같은 취약점입니다.
문제는 Brotli의 압축 해제 코드에 있습니다. 잘못 만들어진 압축 데이터를 받았을 때 오류로 멈추지 않고 “데이터가 더 필요하다”고 판단해 내부 버퍼 밖으로 계속 쓰는 결함입니다. 영향 범위는 Brotli 1.0.3부터 1.2.0까지이고, 수정 버전은 10월 21일 배포된 1.0.10, 1.1.1, 1.2.1입니다.
여기서 핵심은 “Brotli를 쓴다”는 것만으로 위험한 게 아니라, 외부에서 온 Brotli 데이터를 압축 해제하는 경로가 있어야 한다는 점입니다. 웹 서버가 응답을 Brotli로 압축해 보내기만 하는 것은 직접 관련이 적고, 외부 사이트 응답을 받아 자동으로 푸는 크롤러, API 클라이언트, 프록시가 더 위험합니다.
근본 원인은 이미 2026년 2월 27일 Brotli 저장소에서 고쳐졌습니다. 그런데 당시에는 보안 문제로 인식되지 않아 CVE도, 보안 공지도, 새 릴리스도 없었습니다. 이후 OpenAI Codex Security 연구진이 이 수정이 사실은 원격 코드 실행을 막는 패치였다는 것을 확인하면서, 10월 21일에야 정식 릴리스와 함께 공개됐습니다.
보안 업계가 이번 사태에서 주목하는 부분이 바로 이 간격입니다. 원천 코드가 고쳐져도 그 코드를 복사해 내장한 수많은 프로젝트가 각자 새 버전을 내고, 사용자가 그것을 설치하기 전까지는 여전히 취약합니다. Envoy와 Istio처럼 공개 전에 먼저 수정판을 낸 곳도 있었지만, 대부분은 10월 21일 이후에야 움직이기 시작했습니다.
Brotli 취약점 점검에서 가장 많이 묻는 것이 “우리 스택은 해당되나”입니다. 공식 권고로 확인된 것과 아직 확인되지 않은 것을 나눠 정리했습니다.
| 생태계 | 대상 | 영향 | 수정 버전 |
|---|---|---|---|
| 파이썬 | brotli(PyPI) | 영향 | 1.2.1 |
| 파이썬 | brotlicffi | 영향 | 1.2.1.0 |
| 닷넷 | .NET 8 / 9 / 10 런타임 | 영향 | 8.0.33 / 9.0.22 / 10.0.14 |
| Go | andybalholm/brotli | 수정판 배포 | v1.2.7 |
| 런타임 | Bun | 영향 | 1.4.4 |
| 프록시 | Envoy | Brotli 해제 필터 사용 시 | 1.37.8 / 1.38.6 / 1.39.3 / 1.40.0 등 |
| 서비스메시 | Istio | 영향 | 1.29.9 / 1.30.6 / 1.31.2 |
| Rust·Java·PHP | brotli 크레이트, org.brotli, ext-brotli | 공식 권고 미확인 | 각 프로젝트 공지 확인 |
GitHub 보안 권고는 brotli와 brotlicffi 모두 영향을 받는다고 밝혔습니다. 함정은 내가 직접 설치하지 않았어도 들어와 있을 수 있다는 점입니다. urllib3[brotli], aiohttp[speedups] 같은 선택 의존성으로 brotli가 설치되면 requests·httpx·aiohttp가 Brotli 응답을 자동으로 풀어 줍니다. 크롤러나 외부 API를 많이 호출하는 파이썬 서비스라면 파이썬 brotli 업데이트가 1순위입니다.
마이크로소프트는 10월 25일 정기 일정이 아닌 긴급 보안 업데이트를 냈습니다. 런타임 기준 8.0.33, 9.0.22, 10.0.14이며 SDK도 함께 올려야 합니다. BrotliStream, HttpClient 자동 압축 해제, ASP.NET Core 요청 압축 해제가 영향 경로입니다. 특히 런타임을 앱에 포함해 배포하는 self-contained 앱과 NativeAOT 앱은 다시 빌드해야 반영됩니다. Red Hat 설명에 따르면 .NET 8은 Brotli를 내장하고, .NET 9 이후는 리눅스에서 시스템 Brotli를 쓰는 차이도 있습니다.
Go의 andybalholm/brotli는 C 코드가 아닌 순수 Go 포팅인데도, 10월 23일 같은 수정을 옮긴 v1.2.7을 냈습니다. “C 바인딩이 아니니 괜찮다”는 판단이 늘 맞지는 않는다는 사례입니다. 반면 Rust brotli 크레이트는 공식 보안 권고가 아직 확인되지 않았고 커뮤니티 의견도 갈립니다. 공식 공지가 나오기 전까지는 최신 버전을 유지하는 것이 안전합니다.
libbrotli 업데이트는 서버 한 대에서 여러 프로그램을 한 번에 지키는 방법입니다. 시스템 라이브러리에 연결해 쓰는 curl, 파이썬 패키지, RHEL의 Node.js 22 등이 함께 보호됩니다.
| 배포판 | 공지 | 수정 패키지 예시 |
|---|---|---|
| Ubuntu 22.04~26.10 | USN-8956-1 (10월 26일) | 24.04: 1.1.0-2ubuntu0.1 |
| Ubuntu 16.04~20.04 | USN-8956-2 (10월 27일) | Ubuntu Pro(ESM) 필요 |
| Debian 13 trixie | DSA-6582-1 | 1.1.0-2+deb13u1 |
| Debian 12 bookworm | DLA-4855-1 | 1.0.9-2+deb12u1 |
| Alpine 3.23·3.24 | 보안 DB 반영 | brotli-libs 1.2.1-r0 |
| RHEL 8·9·10 | RHSB-2026-015 | brotli 패키지 errata 확인 |
반대로 Brotli를 자체 내장한 프로그램은 OS 패키지를 올려도 그대로입니다. Node.js 공식 바이너리, 닷넷 8, Envoy, Bun, Electron 기반 데스크톱 앱이 대표적입니다. Red Hat은 RHEL의 nodejs:22는 시스템 Brotli를 쓰지만 nodejs:24는 내장본을 쓴다고 구분해 안내했습니다. 같은 서버 안에서도 프로그램마다 고치는 방법이 다르다는 뜻입니다.
Docker Hub 공식 node 이미지에는 22.23.4, 24.22.1, 26.12.1 태그가 올라와 있습니다. 보안 업체 분석에 따르면 slim 계열이 먼저, alpine 계열이 몇 시간 늦게 올라왔다고 하니 10월 21~22일 사이에 빌드한 alpine 이미지는 다시 확인하는 편이 좋습니다. docker build --pull --no-cache로 새로 받고, latest 같은 이동 태그 대신 패치 버전을 고정해 두세요. node:18, node:20, node:25는 지원 종료로 수정 이미지가 나오지 않습니다.
이번 사태로 공지를 낸 상용 제품들을 보면 Brotli가 얼마나 넓게 퍼져 있는지 실감할 수 있습니다.
VS Code, Slack, Discord 같은 Electron 앱의 업데이트 여부는 아직 공식 확인이 어렵습니다. 이런 앱은 자체 사본을 쓰므로 OS가 아니라 각 앱의 업데이트를 받아야 합니다.
# Node.js — 내장 brotli 문자열보다 Node.js 버전 자체가 기준
node -v
# 파이썬
pip show brotli brotlicffi
# 리눅스 패키지
dpkg -l | grep -i brotli # Ubuntu·Debian
rpm -qa | grep -i brotli # RHEL 계열
apk info -v | grep -i brotli # Alpine
# Go 모듈과 빌드된 바이너리
go list -m all | grep -i brotli
go version -m ./app | grep -i brotli
# 닷넷 런타임
dotnet --list-runtimes
# 컨테이너 이미지 스캔
trivy image --severity HIGH,CRITICAL YOUR_IMAGEBrotli 버전 확인에서 주의할 점이 있습니다. Node.js는 수정 코드만 가져와 적용했기 때문에 process.versions.brotli 값이 바뀌지 않을 수 있습니다. AWS도 Lambda의 Node.js 24는 버전 표시만으로 판단하기 어렵다며 런타임 버전 문자열로 확인하라고 안내했습니다. Envoy, Bun처럼 정적으로 묶인 프로그램은 패키지 관리자에 아예 나타나지 않으니, trivy나 syft 같은 SBOM 도구로 이미지를 통째로 스캔하는 것이 확실합니다.
Content-Encoding: br 요청 본문을 막습니다.Accept-Encoding에서 br을 빼되, 악성 서버가 이를 무시할 수 있으니 자동 해제 자체를 끄는 편이 더 안전합니다.미국 CISA는 10월 27일 이 취약점을 실제 악용 취약점(KEV) 목록에 올렸고, 연방 기관 조치 기한은 11월 10일입니다. 보안 업체 ELLIO는 10월 25일 Brotli 대상 스캐닝을, 26일에는 Node.js 24.22.0을 노린 악성 요청을 포착했습니다. 다만 해당 공격 코드는 실험에서 셸 획득에 실패했고, 실패한 시도도 프로세스를 죽여 서비스 장애를 일으킬 수는 있다고 분석했습니다. 반면 Imperva는 공식 Node.js 24.22.0 이미지에서 코드 실행을 시연했다고 밝혀, 원격 코드 실행 자체는 현실적인 위협으로 봐야 합니다.
11월 4일 현재 공개된 기업 피해 사례나 랜섬웨어·봇넷 연계는 확인되지 않았습니다. 국내에서는 KISA 보호나라가 10월 23일 Node.js 보안 업데이트 권고, 10월 26일 Google 제품 보안 업데이트 권고로 이 취약점을 안내했고, 안랩 ASEC도 분석 자료를 냈습니다.
이번 Brotli 취약점의 교훈은 “취약점 이름에 붙은 제품만 고치면 끝”이 아니라는 것입니다. Node2Shell이라는 이름 때문에 Node.js만 보고 넘어갔다면, 파이썬 크롤러와 닷넷 API, 방화벽, 게임 엔진에 같은 결함이 남아 있을 수 있습니다.
정리하면 순서는 세 가지입니다. 첫째, SBOM 스캔과 위 명령으로 서버·컨테이너·개발 PC의 Brotli 버전을 확인합니다. 둘째, 외부 데이터를 푸는 서비스부터 언어별 수정 버전과 libbrotli 업데이트를 적용하고 이미지를 다시 빌드합니다. 셋째, 10월 25일 이후 로그에서 비정상 종료와 낯선 자식 프로세스를 점검합니다. CVE-2026-111497은 이미 실제 공격 단계에 들어선 만큼, CISA 기한인 11월 10일을 우리 조직의 기준선으로 삼아도 늦지 않습니다.