
Tailscale, Python에 이어 이번에는 Node.js입니다. 10월 21일 Node.js가 22.x, 24.x, 26.x 전체 라인에 보안 릴리스를 냈고, 그중 Brotli 압축 해제 취약점(CVE-2026-111497, 일명 Node2Shell)은 원격 코드 실행까지 가능한 데다 공개 PoC까지 나와 있습니다.
무엇이 문제인지, 내 환경 확인 방법, 그리고 설치 방식별 업그레이드 방법을 쉘 명령 위주로 정리했습니다.
3줄 요약
1. Node.js를 22.23.4 / 24.22.1 / 26.12.1 이상으로 올리세요. 20.x 이하, 23.x, 25.x는 패치가 없으니 지원 버전으로 옮겨야 합니다.
2. 취약한 코드는 Node.js 런타임 안에 있어서 npm 패키지 업데이트나 npm audit으로는 해결도, 탐지도 안 됩니다. Docker 이미지, CI, 서버리스까지 런타임을 직접 올려야 합니다.
3. undici를 npm 패키지로 직접 쓴다면 6.29.1 / 7.30.1 / 8.12.2로 함께 올리세요.

1. 무엇이 문제인가
이번 릴리스에서 10건이 수정됐고, 이 중 High 등급이 4건입니다.
Brotli 디코더 메모리 범위 밖 쓰기 (CVE-2026-111497, High)
Node.js에 내장된 Brotli 디코더가 조작된 압축 데이터를 받으면 내부 8바이트 버퍼 밖으로 데이터를 계속 써서 메모리가 오염됩니다. 결과적으로 크래시, 메모리 노출, 최악의 경우 원격 코드 실행까지 이어질 수 있습니다. CVSS 4.0 기준 9.2(Critical)로 평가됐고, Imperva가 공식 Node.js 이미지에서 코드 실행을 시연했으며 GitHub에 공개 PoC도 올라와 있습니다.
특히 위험한 경우: 내가 만들지 않은 Brotli 데이터를 푸는 모든 서비스
- 외부 URL을 가져오는 앱: Node 내장
fetch()는Content-Encoding: br응답을 자동으로 풀어줍니다. 웹훅, 링크 미리보기, 크롤러, "URL에서 가져오기" 기능 모두 해당됩니다. - 요청 본문을 받는 서버: Express 5.0.0~5.3.0, body-parser 2.0.0~2.3.0은 Brotli 요청 본문을 풀어줍니다. 단, 버그는 Express가 아니라 Node 런타임에 있으므로 Express를 올려도 해결되지 않습니다.
zlib.brotliDecompress*(),createBrotliDecompress()를 직접 쓰는 코드
Node.js는 Brotli를 자체 번들하기 때문에 OS의 libbrotli를 업데이트해도 소용없고, Node.js 버전만이 기준입니다.
TLS 인증서 호스트 검증 우회 (CVE-2026-105483, CVE-2026-106069, High)
tls.checkServerIdentity()가 127.0.0.1(전각 숫자)이나 0127.0.0.1(앞자리 0) 같은 비정규 IP 표기를 제대로 처리하지 못해서, 다른 주소용 인증서로 연결이 인증될 수 있었습니다. 사용자가 입력한 호스트로 대신 접속하는 서비스(웹훅, 프록시 등)가 주로 영향을 받습니다.
DNS 호스트명 잘림 (CVE-2026-105482, High)
dns.resolve*()와 dns.reverse()에 NUL 문자가 들어간 호스트명을 넘기면 NUL 앞까지만 조회됩니다. 예를 들어 attacker.example\0.trusted.example은 허용 목록 검사는 통과하지만 실제로는 attacker.example을 조회합니다. dns.lookup()은 해당되지 않습니다.
그 외 (Medium 3건, Low 3건)
- HTTP keep-alive 소켓 재사용 시 요청 전에 도착한 응답을 정상 응답으로 처리 (CVE-2026-105481)
0177.0.0.1,127.1같은 표기로 DNS SAN만 있는 인증서가 IP 엔드포인트를 인증 (CVE-2026-106072)--build-snapshot이나 SEA 스냅샷에crypto.randomUUID()/randomInt()난수 캐시가 저장돼 프로세스마다 같은 값이 나옴 (CVE-2026-106074)- zlib.ZipFile, Permission Model(
--allow-fs-read와일드카드,fs.realpathSync) 관련 Low 3건 - 함께 업데이트된 의존성: c-ares, OpenSSL(22.x/24.x), SQLite(22.x), undici, 그리고 eMudhra 중간 CA 유출 대응 인증서 목록
2. 내 환경 확인하기
패치 버전보다 낮으면 업그레이드 대상입니다. 서버, 개발 PC, CI 러너, Docker 이미지, 서버리스 등 Node가 도는 곳을 모두 확인하세요.
# 현재 사용 중인 Node 버전 (22.23.4 / 24.22.1 / 26.12.1 미만이면 취약)
node -v
# PATH에 잡힌 node가 여러 개인지 확인
which -a node
# nvm으로 설치된 전체 버전
nvm ls
# Node에 번들된 Brotli 및 주요 라이브러리 버전
node -p "process.versions.brotli"
node -p "process.versions"
# Docker 이미지 안의 Node 버전
docker run --rm --entrypoint node <image> --version
Node.js 공식 도구로 현재 버전이 알려진 취약점에 해당하는지 바로 확인할 수도 있습니다.
npx is-my-node-vulnerable
3. 해결 방법: Node.js 업그레이드
업그레이드가 유일한 완전한 해결책입니다. Node.js는 별도 우회책(workaround)을 내지 않았습니다. 사용하는 설치 방식만 골라서 실행하세요. (예시는 24.x 기준이고, 22.x는 22.23.4, 26.x는 26.12.1로 바꾸면 됩니다.)
# nvm
nvm install 24.22.1
nvm alias default 24.22.1
nvm use 24.22.1
# fnm
fnm install 24.22.1
fnm default 24.22.1
# volta
volta install node@24.22.1
# n
sudo n 24.22.1
# Homebrew (macOS)
brew update && brew upgrade node # node@22, node@24 를 쓰면 해당 formula 이름으로
# NodeSource 저장소 (Ubuntu / Debian)
sudo apt update && sudo apt install --only-upgrade nodejs
업그레이드 후에는 실행 중인 프로세스를 반드시 재시작해야 새 런타임이 적용됩니다. PM2는 데몬 자체가 이전 Node로 떠 있으므로 pm2 update로 데몬부터 새로 띄워야 합니다.
# PM2
pm2 update
pm2 restart all
# systemd 서비스
sudo systemctl restart <your-app>.service
# 확인
node -v
Docker 이미지
베이스 이미지 태그를 패치 버전으로 올리고 캐시 없이 다시 빌드한 뒤 재배포합니다. node:24-slim처럼 메이저 태그만 쓰더라도 --pull로 새 이미지를 받아야 반영됩니다.
# Dockerfile
# FROM node:24.22.1-slim
docker build --pull --no-cache -t myapp:latest .
# 기존 컨테이너 교체 후 버전 확인
docker run --rm --entrypoint node myapp:latest --version
CI (GitHub Actions)
- uses: actions/setup-node@v4
with:
node-version: '24.x'
check-latest: true # 캐시된 구버전 대신 최신 패치 사용
package.json으로 하한선 고정
팀 프로젝트라면 engines로 최소 버전을 정해두고 engine-strict를 켜두면 취약 버전으로 설치하는 것을 막을 수 있습니다.
{
"engines": {
"node": ">=22.23.4 <23 || >=24.22.1 <25 || >=26.12.1"
}
}
echo "engine-strict=true" >> .npmrc
AWS Lambda 같은 서버리스는 관리형 런타임이 순차적으로 갱신됩니다. CloudWatch 로그의 INIT_START Runtime Version으로 실제 적용 버전을 확인하고, 컨테이너 이미지로 배포했다면 직접 다시 빌드해야 합니다.
4. undici npm 패키지도 함께 업데이트
Node 내장 fetch()는 런타임 업그레이드로 함께 갱신되지만, undici를 npm 패키지로 직접 설치해 쓰고 있다면 별도로 올려야 합니다. 10/15에 악성 서버 응답 하나로 프로세스를 죽일 수 있는 DoS 취약점 등이 공개됐습니다(CVE-2026-109869 등).
# 어떤 패키지가 undici를 쓰는지 확인
npm ls undici
# 패치 버전으로 업데이트 (사용 중인 메이저에 맞게: 6.29.1 / 7.30.1 / 8.12.2)
npm install undici@^7.30.1
# 의존성 취약점 전체 점검
npm audit --omit=dev
npm audit fix
5. 바로 업그레이드할 수 없다면 (임시 완화)
아래는 Qualys 위협연구팀이 제시한 임시 완화책입니다. 특정 공격 경로를 좁혀줄 뿐 패치를 대신하지는 못합니다.
- 신뢰할 수 없는 Brotli 데이터는 가능한 한 풀지 않고, 리버스 프록시나 WAF에서 압축 본문 크기와 압축 비율을 제한합니다.
- 사용자가 입력한 호스트명은
dns.resolve*대신dns.lookup으로 조회하고, NUL 문자가 들어간 호스트명은 거부합니다. - TLS 연결 전에 IP 주소를 정규 형식으로 바꾸고, 전각 숫자 등 비정규 표기는 거부합니다.
nginx를 앞단에 두고 있다면 Brotli로 압축된 요청 본문을 거부해서 인바운드 경로를 막을 수 있습니다.
# nginx server 블록 안에 추가
if ($http_content_encoding ~* "br") {
return 415;
}
sudo nginx -t && sudo systemctl reload nginx
외부로 요청하는 쪽(fetch 응답)은 상대 서버가 Brotli를 마음대로 보낼 수 있어서 Accept-Encoding에서 br을 빼도 효과가 제한적이고, WAF도 앱이 받아오는 응답은 보지 못합니다. 이 경로는 런타임 업그레이드로만 막을 수 있습니다.
코드 레벨에서는 사용자 입력 호스트를 이렇게 한 번 걸러주면 TLS/DNS 우회 경로를 줄일 수 있습니다.
const net = require('node:net');
function assertSafeHost(host) {
if (host.includes('\0')) throw new Error('NUL in hostname');
// 전각 숫자 등 비ASCII 문자 거부 (IDN은 미리 punycode로 변환해서 사용)
if (/[^\x00-\x7F]/.test(host)) throw new Error('non-ASCII hostname');
// 숫자와 점으로만 된 호스트나 0x로 시작하는 호스트는 정규 IPv4만 허용
// (0127.0.0.1, 127.1, 2130706433, 0x7f.0.0.1 등 거부)
if ((/^[0-9.]+$/.test(host) || /^0x/i.test(host)) && !net.isIPv4(host)) {
throw new Error('non-canonical IPv4');
}
return host;
}
6. 지원 종료(EOL) 버전 사용 중이라면
Node.js 20.x 이하와 23.x, 25.x 같은 홀수 버전은 이번 수정이 나오지 않았고 앞으로도 나오지 않습니다. 지원 라인(22.x, 24.x, 26.x)으로 옮기는 것만이 해결책입니다. 참고로 Node.js 26은 10월 28일 LTS로 전환될 예정이니, 새로 옮긴다면 24.x LTS나 26.x를 권장합니다.
# 예: 20.x → 24.x 이전
nvm install 24
nvm alias default 24
rm -rf node_modules package-lock.json # 네이티브 모듈 재빌드를 위해 (lock 파일 재생성은 팀과 협의)
npm install
npm test
7. 체크리스트
| 점검 항목 | 확인 명령 | 조치 기준 |
| Node 버전 | node -v |
22.23.4 / 24.22.1 / 26.12.1 이상 |
| EOL 버전 | nvm ls |
20.x 이하, 23.x, 25.x는 지원 라인으로 이전 |
| 실행 중 프로세스 | pm2 update, systemctl restart |
업그레이드 후 반드시 재시작 |
| Docker 이미지 | docker run ... node --version |
베이스 이미지 갱신 후 --pull --no-cache 재빌드 |
| CI 러너 | setup-node 설정 |
check-latest: true |
| undici 패키지 | npm ls undici |
6.29.1 / 7.30.1 / 8.12.2 이상 |
| 인바운드 Brotli | nginx / WAF | 패치 전까지 br 요청 본문 거부 |
| 취약 여부 자동 확인 | npx is-my-node-vulnerable |
결과에 취약점이 없을 때까지 |
참고 자료
- Node.js - Wednesday, October 21, 2026 Security Releases
- Qualys - Node.js Patches Brotli Remote Code Execution Flaw
- Broccoli2Shell / Node2Shell - Brotli CVE-2026-111497
- HKCERT - Node.js Multiple Vulnerabilities
- undici Security Advisories
- (이전 글) 2026년 10월 Python 보안 이슈 정리
※ 이 글은 2026년 10월 27일 기준 공개된 정보를 정리한 것입니다. 이후 추가 릴리스가 나올 수 있으니 Node.js 보안 공지를 함께 확인해주세요.
'프로그래밍 > 기타' 카테고리의 다른 글
| 2026년 10월 Python 보안 이슈 정리 (CPython, Brotli, Pillow, LiteLLM)와 대응 방법 (0) | 2026.10.27 |
|---|---|
| Tailscale SSH 보안 이슈(Tailworm) 정리 및 패치 전 대응 방법 (0) | 2026.10.27 |
| 한 개의 키보드/마우스로 여러 컴퓨터를 멀티 모니터 처럼 사용하기 (0) | 2024.05.26 |
| 집 PC/서버를 이용한 Public Service 구성 (2) | 2024.01.01 |
| VS Code 열려있는 창 모두 닫기 (전체 단축키 다운로드) (0) | 2023.03.20 |