약 5분

Next.js 이미지 최적화 원격 코드 실행 CVE-2026-96889 점검

이 글의 순서 6개 절
  1. 무엇이 깔려 있나
  2. 올리기
  3. OS 패키지를 올려도 안 고쳐진다
  4. 호스팅 플랫폼에 올린 앱
  5. 앞단 TLS와 키
  6. 참조

2026-10-14에 Next.js가 정기 일정 밖의 보안 업데이트를 냈다. 고친 버전은 16.4.1(Active LTS)과 15.5.28(Maintenance LTS)이다. 세 건 중 두 건이 인증 없는 원격 코드 실행이다.

권고 심각도 영향 버전 조건
CVE-2026-96889 (GHSA-2464-fhr4-w9xw) Critical 9.5 10.0.0~15.5.27, 16.4.0 이하 16.x 이미지 최적화 API가 SVG를 처리. glibc 리눅스
GHSA-673q-mjm6-r5xr Critical 9.5 16.0.0~16.4.0 App Router + Cache Components. use cache 함수가 typed array 여러 개를 반환
GHSA-p3m5-59mp-jv5r High 8.2 13.0.015.5.27, 16.0.016.4.0 App Router에 Server Action이 하나라도 있음. CPU를 붙잡아 서비스 거부

use cache 건은 우회책이 없다. Next.js는 그런 함수가 없어도 바로 올리라고 했다. Server Action 건도 업그레이드 말고는 방법이 없다. 이미지 최적화 건만 우회책이 있다. Next.js가 쓰는 sharp를 0.35.5 이상으로 맞추는 것이다.

무엇이 깔려 있나

npm ls next sharp

Next.js 15부터는 next가 sharp를 선택 의존성으로 끌고 온다. output: 'standalone'으로 빌드해 이미지에 담았다면 소스 폴더가 아니라 실제로 배포된 쪽을 본다.

# 빌드 결과물
find .next/standalone -path '*node_modules/sharp/package.json' -exec grep -H '"version"' {} +

# 돌고 있는 컨테이너: 경로를 찾은 뒤 그 파일의 version을 본다
docker exec <컨테이너> find / -path '*node_modules/sharp/package.json' -not -path '/proc/*' 2>/dev/null
docker exec <컨테이너> grep '"version"' /app/node_modules/sharp/package.json

C 라이브러리도 확인한다.

ldd --version 2>&1 | head -1

GLIBC나 GNU libc가 보이면 glibc, musl libc가 보이면 Alpine 계열이다. Node 공식 이미지 중 -alpine이 붙지 않은 것(node:22, node:22-slim)은 Debian 기반이라 glibc다. 권고문은 glibc 리눅스를 조건으로 적었다. Alpine은 이 조건 밖이지만 나머지 두 건 때문에 어차피 올려야 한다.

올리기

npm install next@16.4.1    # 16.x
npm install next@15.5.28   # 15.x
npm ls sharp               # 0.35.5 이상인지

Next.js는 패치 버전으로 올리면 패키지 관리자가 고친 sharp를 같이 가져온다고 했다. 잠금 파일이 옛 sharp를 붙잡고 있을 수 있으니 npm ls로 확인한다. 그다음 다시 빌드하고 배포한다. 컨테이너라면 이미지를 새로 굽고 교체해야 반영된다.

13.x와 14.x에는 고친 버전이 없다. 이미지 최적화 건은 sharp를 0.35.5 이상으로 강제해 막는다. App Router에서 Server Action을 쓰고 있다면 서비스 거부 건이 남으니 15.5.28 이상으로 올려야 한다.

{
  "overrides": { "sharp": "^0.35.5" }
}

올린 뒤에 이미지가 실제로 변환되는지 본다. 200과 image/ 형식이 나오면 된다.

curl -s -o /dev/null -w '%{http_code} %{content_type}\n' \
  'https://example.com/_next/image?url=%2Flogo.png&w=64&q=75'

OS 패키지를 올려도 안 고쳐진다

sharp는 보통 libvips와 그 의존 라이브러리를 묶은 미리 빌드된 바이너리(@img/sharp-libvips-linux-x64 같은 패키지)로 깔린다. 그래서 apt upgrade로 시스템 라이브러리를 올려도 이 바이너리는 그대로다. 시스템 libvips를 쓰도록 직접 빌드한 경우가 아니면 npm 패키지를 올려야 고쳐진다.

호스팅 플랫폼에 올린 앱

이미지 최적화를 플랫폼이 따로 처리하면 사정이 다르다. Netlify는 Next.js 이미지 경로를 자기 이미지 CDN이 받아서 이 건이 쓰이지 않는다고 밝혔다. use cache 건은 앱 코드에서 생기니 어디서 돌든 버전을 올린다. Netlify는 공개된 배포 미리보기와 브랜치 배포도 지우라고 했다. 자동으로 지워질 때까지 옛 버전으로 남기 때문이다.

앞단 TLS와 키

Next.js를 직접 운영하면 보통 Nginx나 로드밸런서가 TLS를 받고, Node는 뒤에서 HTTP로 돈다. 원격 코드 실행에 성공한 공격자는 Node 프로세스 권한을 얻는다. 인증서 키가 앞단에만 있고 그 사용자가 읽을 수 없으면 키 파일을 바로 읽지는 못한다.

키를 앱 이미지에 같이 구웠거나, Node가 직접 HTTPS를 받거나, 앱 사용자가 읽는 경로에 키를 뒀다면 다르다. 환경 변수에 넣어 둔 API 키와 DB 비밀번호도 같은 처지다. 패치 전에 이상한 흔적이 있으면 인증서 개인키 교체와 폐기 순서대로 처리한다.

참조