2026년 5월 Next.js 취약점 13건, 무엇부터 패치해야 할까

2026년 5월 공개된 Next.js 취약점 13건을 Vercel·Cloudflare 운영 환경 기준으로 분석합니다. 실제 노출되는 RSC 캐시 포이즈닝 위험, 16.2.6·15.5.18 업그레이드 순서, 배포 후 엣지 캐시 퍼지와 검증 항목까지 한 번에 정리했습니다.

Saturday, September 5, 2026Omid Saffari
2026년 5월 Next.js 취약점 13건, 무엇부터 패치해야 할까

Threads에 퍼진 캡처 화면 속 Next.js 취약점은 클라우드 메타데이터 엔드포인트까지 닿는 WebSocket SSRF, CVE-2026-44578입니다. 하지만 제 스택에서는 공격 경로가 열리지 않으므로 여기에만 촉각을 곤두세우는 것은 우선순위를 잘못 잡는 일입니다. 정작 독자가 보는 모든 페이지를 오염시킬 수 있는 취약점은 아무도 캡처해 올리지 않는 Moderate 등급 항목입니다.

한 문장으로 정리한 상황과 정말 중요한 대응 순서

Next.js는 2026년 5월 6일 보안 권고 13건을 한꺼번에 공개했고, 5월 7일에는 패치 버전 16.2.6과 15.5.18을 내놓았습니다. 언론의 관심은 WebSocket SSRF인 CVE-2026-44578에 쏠렸습니다. 장면이 강렬하기 때문입니다. 공격자가 연결을 업그레이드하면 서버가 스스로 169.254.169.254를 찾아가고, 메타데이터 엔드포인트가 IAM 자격 증명을 돌려줍니다.

그러나 이 취약점은 셀프 호스팅 환경에만 해당합니다. 앞단에 Cloudflare를 둔 Vercel 관리형 배포에서는 해당 경로에 접근할 수 없습니다. 제 독자에게 실제로 닿을 수 있는 것은 거의 언급되지 않는 Moderate 등급의 RSC 캐시 포이즈닝 취약점입니다. 오염된 RSC 응답이 공격자 요청 한 번으로 끝나지 않고 공유 엣지 캐시에 들어가, 태그를 퍼지할 때까지 모든 방문자에게 제공되기 때문입니다.

Next.js >= 13.4.13을 사용한다면 이번 주 안에 업그레이드해야 합니다. 남는 질문은 어느 버전으로, 어떤 순서로 올리고, 그 뒤 무엇을 다시 테스트해야 하는가뿐입니다. 아래 내용은 App Router를 Vercel에 배포하고, 앞단의 Cloudflare에서 Cache-Tag 퍼지와 revalidate 웹훅을 사용하는 omidsaffari.com 스택에 직접 적용한 트리아지 매트릭스입니다. 이 점검을 거치자 헤드라인을 장식한 CVE가 아니라 다른 항목을 먼저 처리해야 한다는 결론이 나왔습니다.

비기술 창업자가 알아야 할 핵심

제품이 Next.js 앱이라면 이 사안은 백로그에 넣어둘 티켓이 아니라 이번 주에 처리할 항목입니다. 비용의 핵심은 지연 시간이나 개발자 투입 시간이 아닙니다. 캐시된 페이지가 오염되거나 인증을 우회해 관리자 경로에 들어가는 순간 고객 신뢰의 문제가 됩니다. 마케팅 페이지에 공격자가 심은 콘텐츠가 나타난 캡처 한 장, 세션 없이 /admin에 접근했다는 제보 한 건이면 판매에 집중해야 할 다음 주를 해명하는 데 쓰게 됩니다.

엔지니어에게는 딱 하나만 물으면 됩니다. “현재 16.2.6 또는 15.5.18을 사용하고 있고, 배포 후 엣지 캐시까지 퍼지했습니까?” “패치 중입니다”, “진행하고 있습니다”, “SSRF는 우리에게 영향이 없습니다”처럼 답이 모호하다면 아직 끝난 것이 아닙니다. 직접 운영하는 서버에서 next start를 실행하는 셀프 호스팅 배포(ECS, EC2, Kubernetes 등)는 다른 모든 취약점에 더해 SSRF에도 노출되므로 위험도가 더 높습니다. 우리 환경이 어느 쪽인지도 물어보십시오. 답변에는 10초면 충분합니다.

Next.js 취약점 13건, 실제 스택에 닿는 것만 추리기

요즘 올라오는 “CVE 13건, 지금 당장 패치” 식의 글은 목록을 모두 같은 위험으로 취급한다는 점에서 잘못됐습니다. 실제로는 그렇지 않습니다. 각 권고에는 셀프 호스팅, Turbopack, Cache Components, CSP nonce, i18n 같은 전제 조건이 있습니다. 배포 환경과 조건이 맞아떨어지는 항목만 실제 노출 범위입니다. 같은 13건을 공격이 성립하는 조건별로 다시 묶으면 다음과 같습니다.

미들웨어·프록시 우회 그룹(5건, 대부분 High). 가장 큰 항목은 GHSA-267c-6grr-h53f입니다. App Router의 segment-prefetch URL이 미들웨어 인증 검사를 우회해 라우팅되도록 만들 수 있습니다. 이 그룹에는 Turbopack을 거칠 때 남아 있던 불완전한 수정 사항을 보완하기 위해 5월 7일 공개된 후속 권고(GHSA-26hh-7cqf-hhc6), Pages Router i18n 기본 로캘 경로 우회, 동적 라우트 매개변수 주입 우회도 포함됩니다. 전제 조건은 인증이나 rewrite를 Next.js 미들웨어로 강제하는 것입니다. 대부분의 앱이 여기에 해당합니다.

SSRF, CVE-2026-44578. 셀프 호스팅 서버의 WebSocket 업그레이드 핸들러가 클라우드 메타데이터를 포함해 80번 포트의 내부 HTTP 엔드포인트에 접근합니다. 영향 범위는 13.4.13+부터 <15.5.16까지, 그리고 16.0.0–<16.2.5이며 셀프 호스팅에만 해당합니다. Vercel 관리형 배포에는 영향이 없다는 점이 확인됐습니다.

서비스 거부(DoS), 2건. 하나는 업스트림 RSC DoS이고, 다른 하나는 Cache Components를 사용하도록 설정한 앱에서 발생하는 연결 고갈 DoS(High, GHSA-q4gf-8mx6-v5v3)입니다. 두 번째 항목은 Cache Components를 명시적으로 활성화해야 성립하며, 아직 이를 사용하지 않는 앱이 대부분입니다.

RSC 캐시 포이즈닝(Moderate). RSC 페이로드 파이프라인에서 cache-busting 값이 충돌하면 조작된 요청으로 캐시 응답을 오염시킬 수 있습니다. 전제 조건은 CDN, 리버스 프록시, Vercel 엣지, Cloudflare 등 어느 다운스트림에서든 RSC 응답을 캐시하는 것입니다. 제 스택에서 가장 중요한 항목입니다.

XSS, 2건. CSP nonce를 생성하는 App Router 앱의 CVE-2026-44581(Moderate), 그리고 신뢰할 수 없는 입력을 받는 beforeInteractive 스크립트의 XSS입니다. CSP를 nonce와 함께 제공하거나 사용자 입력을 beforeInteractive 스크립트 태그에 전달할 때 공격 조건이 성립합니다.

배포 환경이 어떤 전제 조건을 충족하는지 적어보면 실제 대응 목록이 나옵니다. omidsaffari.com의 경우 미들웨어 우회 그룹(해당), SSRF(비해당, Vercel 호스팅), RSC DoS(해당, 업스트림), Cache Components DoS(비해당, 사용 설정 안 함), RSC 캐시 포이즈닝(해당하며 영향 증폭), CSP nonce XSS(해당), beforeInteractive XSS(비해당)입니다. High/Moderate 권고 8건을 제 환경에 대입하면 5건만 남고, 피해 범위가 가장 넓은 것은 널리 알려진 취약점이 아닙니다.

화제가 된 SSRF보다 RSC 캐시 포이즈닝을 먼저 봐야 하는 이유

CVE-2026-44578이 성립하려면 Node 측 next start 서버가 WebSocket 업그레이드를 처리하고 리디렉션을 따라가야 합니다. Vercel에서는 제 요청이 SSRF 경로에 닿는 방식으로 해당 서버를 통과하지 않습니다. 플랫폼이 WebSocket을 종료하며, 메타데이터 엔드포인트도 이 경로로는 유지될 수 없는 홉 제한이 적용된 IMDSv2 뒤에 있습니다. 앞단에 Cloudflare를 둬도 결론은 달라지지 않습니다. Vercel + Cloudflare 구성에서는 화제의 캡처와 같은 결과가 나오지 않습니다.

RSC 캐시 포이즈닝 권고는 정반대입니다. 모든 사용자가 거치는 요청 처리 경로에 취약점이 있고, 그 결과 독자에게 전송되는 직렬화된 React 트리인 RSC 페이로드가 오염됩니다. 제 사이트에서는 이 페이로드가 공격자의 요청에만 머물지 않고 다음 위치에 저장됩니다.

Http
Cache-Control: public, s-maxage=300, stale-while-revalidate=86400
Cache-Tag: article:nextjs-may-2026-security-triage

s-maxage=300은 Cloudflare가 해당 응답을 5분 동안 보관한다는 뜻입니다. stale-while-revalidate=86400을 설정했으므로 만료된 뒤에도 백그라운드에서 revalidate하는 동안 하루 동안 계속 응답을 제공합니다. Cache-Tag는 제가 퍼지에 사용하는 수단입니다. 새 리비전을 게시하면 revalidate 웹훅이 태그 퍼지를 실행하고 객체가 제거됩니다.

응답이 오염될 때 어떤 일이 벌어지는지 순서대로 보겠습니다. 공격자가 cache-busting 충돌을 악용하는 라우트에 접근합니다. Cloudflare는 캐시할 수 있는 응답이라고 판단해 해당 URL의 캐시 키로 저장하고 article:<slug> 태그를 붙입니다. 이후 그 슬러그에 방문하는 모든 사람은 엣지에서 오염된 RSC 페이로드를 받습니다. 오리진을 거치지도, 미들웨어를 통과하지도 않으며, 10분 뒤에 추가한 WAF 규칙의 검사 대상도 아닙니다. 오염된 객체는 이미 다운스트림에 있습니다. s-maxage 기간이 끝나거나 게시 파이프라인이 퍼지를 실행할 때까지 300개 PoP에 남아 있습니다. 이미 캐시된 응답에는 오리진의 WAF 규칙이 아무 효과도 없습니다.

그래서 위험에 놓이는 자산은 내부 API가 아니라 게시된 콘텐츠 표면, 즉 콘텐츠 엔진의 출력물입니다. 13건의 권고 목록에서는 SSRF가 High, 이 취약점이 Moderate로 분류됩니다. 하지만 RSC 앞단에 캐시가 있는 사이트라면 실질적인 영향의 우선순위는 뒤집힙니다.

정확한 업그레이드 경로와 Turbopack 후속 패치 함정

첫 단계는 현재 버전을 정확히 확인하는 것입니다. caret 범위를 사용하면 package.json만으로 실제 설치 버전을 알 수 없으므로 lockfile을 확인해야 합니다.

Bash
bun pm ls | grep next
# or
npm ls next

그다음 Next.js 16 계열은 16.2.6, Next.js 15 계열은 15.5.18로 정확히 고정합니다. 16.2.5도, 15.5.16도 아닙니다.

Bash
bun add next@16.2.6
# or
npm install next@16.2.6 --save-exact

정확한 버전 고정이 중요한 이유는 거의 언급되지 않는 함정 때문입니다. 최초 권고 13건에 대한 패치는 5월 6일 16.2.5 / 15.5.16에 반영됐습니다. 그러나 5월 7일 Vercel은 GHSA-26hh-7cqf-hhc6을 공개했습니다. segment-prefetch 미들웨어 우회가 Turbopack을 통해 요청을 처리할 때 여전히 악용될 수 있어 불완전한 수정 사항을 보완한 후속 권고입니다. Turbopack을 쓰지 않는 사용자는 16.2.5 / 15.5.16에서 보호됐지만, Turbopack 사용자는 그렇지 않았습니다. 완전한 수정 버전은 16.2.6 / 15.5.18입니다.

13.x 또는 14.x 계열을 사용 중이라면 기다려도 패치는 나오지 않습니다. Vercel은 백포트를 제공하지 않습니다. 해결책은 15.x 또는 16.x로 마이그레이션하는 것이며, 단순한 bun update가 아니라 마이그레이션 작업으로 범위를 잡아야 합니다. 규모가 있는 앱이라면 최소 1주는 배정하십시오. 특히 미들웨어 matcher와 App Router의 기본 캐싱 동작 주변에서 실제 호환성 문제가 생길 수 있습니다.

배포가 끝나면 엣지 캐시를 퍼지해야 합니다. 이번 주에 읽은 다른 글에서는 모두 빠져 있던 단계입니다. 퍼지하지 않으면 패치 적용 전에 Cloudflare가 저장한 RSC 페이로드가 그대로 남습니다. 취약 기간에 누군가 공격했다면 이미 오염됐을 수도 있으며, s-maxage가 만료될 때까지 엣지에서 계속 제공됩니다. 제 스택에서는 다음과 같이 처리합니다.

Bash
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE/purge_cache" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"purge_everything": true}'

이후 검증도 필요합니다. 미들웨어 인증으로 보호되는 라우트를 하나 고른 뒤, 권고문에 나온 segment-prefetch URL 형태를 만들어 프로덕션에 재전송합니다. 인증되지 않은 요청에는 미들웨어가 설정한 401 / 302 등의 응답이 와야 합니다. 콘텐츠와 함께 200이 반환된다면 미들웨어 패치가 적용되지 않은 것이므로 Next.js가 아니라 배포에 문제가 있습니다.

16.2.6 적용 후 달라진 점

솔직히 말해 이번 릴리스는 보안 수정만 담긴 패치가 아닙니다. 취약점 자체가 라우팅과 캐시 키 동작에 있었기 때문에 여러 패치가 그 동작을 의도적으로 바꿉니다. 따라서 차이가 발생할 가능성을 염두에 둬야 합니다.

.rsc 및 segment-prefetch URL의 미들웨어 matcher 강화. segment-prefetch 우회 수정으로 matcher의 매칭 방식이 달라졌습니다. 이전 URL 형태를 전제로 만든 matcher 패턴이 있다면 다시 검증해야 합니다. 특히 negative lookahead로 _next 경로를 제외하면서 .rsc는 항상 그 아래에 있다고 가정했다면 주의가 필요합니다. 저는 보호된 라우트의 segment-prefetch 접미사를 명시적으로 잡도록 matcher 하나의 범위를 넓혀야 했습니다. 5분이면 고칠 수 있었지만 replay 테스트를 하지 않았다면 조용히 숨어 있을 회귀 문제였습니다.

CSP nonce 처리. XSS 수정(CVE-2026-44581)은 App Router 렌더링 과정에서 nonce가 전달되는 방식을 바꿉니다. 미들웨어에서 nonce를 만들고 Script 컴포넌트에서 참조한다면 배포 후 하루 동안 CSP를 report-only 모드로 실행하십시오. 제 환경에서는 위반이 없었지만 변경 자체는 분명하며, nonce 생성 흐름을 수정해야 했던 한 팀의 사례도 들었습니다.

Cache Components 동작 변경. Cache Components에서 연결 고갈 DoS 수정 경로를 사용한다면, 부하가 걸렸을 때 캐시 미스를 병합하는 방식이 달라집니다. 트래픽이 집중되는 경로를 다시 테스트해야 합니다. 저는 Cache Components를 사용하지 않아 해당 작업이 필요 없었습니다.

업그레이드를 완료로 판단하기 전에 실행한 검증 체크리스트는 다음과 같습니다.

Text
[ ] next version pinned to 16.2.6 in lockfile
[ ] middleware auth replay on /admin via segment-prefetch URL  401
[ ] middleware auth replay via .rsc URL  401
[ ] RSC cache key sanity: same URL, two clients, identical payload
[ ] forced edge purge after deploy
[ ] CSP report-only on for 24h with no new violations
[ ] one full revalidate cycle on a high-traffic page

스테이징 점검 시간을 따로 잡으십시오. 보안 수정이 라우팅과 캐시 키 동작을 의도적으로 변경하므로 위험이 전혀 없는 패치 업데이트가 아닙니다. 화요일 오후의 평범한 배포가 화요일 밤의 장애로 번지지 않게 하는 차이는 프로덕션 전환 전에 인증 경로를 replay 테스트했느냐에 달려 있습니다.

WAF가 아니라 패치가 답이라는 구조적 교훈

Vercel은 이번 릴리스에 WAF 규칙을 내놓지 않았습니다. 변경 기록에는 패치만이 유일하게 완전한 완화책이라고 명시돼 있으며, 그 판단이 맞습니다. 악성 요청 형태가 정상 요청과 너무 많이 겹치기 때문에 이 권고들은 어느 것도 엣지에서 깔끔하게 필터링할 수 없습니다. Cloudflare는 5월 6일 WAF 규칙과 프레임워크 어댑터 완화책을 심층 방어 수단으로 배포했지만, 업그레이드를 대신하는 조치는 아닙니다. 표현을 정확히 읽어야 합니다. Cloudflare 변경 기록은 WAF 규칙이 이후의 대체책이 아니라 롤아웃 기간의 노출을 줄이는 수단이라고 분명히 밝힙니다.

이번 주 사례가 주는 구조적 교훈은 바로 이 차이에 있습니다. 프레임워크 업그레이드와 재배포, 엣지 캐시 퍼지를 오후 한나절에 끝낼 수 있는 스택이라면 CVE 13건의 동시 공개도 화요일에 처리할 작업 하나가 됩니다. 반대로 업그레이드에 마이그레이션이 필요하거나, 퍼지 수단이 없거나, 배포 파이프라인에 수동 게이트가 있어 이 과정을 빠르게 돌릴 수 없다면 롤아웃이 끝날 때까지 노출됩니다. 며칠, 때로는 몇 주가 걸립니다.

이는 에이전트 피해 범위에 관한 논지와 같은 패턴입니다. 사후 필터링보다 구조적으로 빠른 복구 능력이 중요합니다. 모든 악성 요청이나 모든 잘못된 에이전트 호출을 막는 것이 목표가 아닙니다. 하나가 실제로 들어왔을 때 대응 시간을 분 단위로 줄이고, 막연한 기대가 아니라 토폴로지로 피해 범위를 제한할 수 있게 시스템을 설계해야 합니다.

오늘 밤 업그레이드하십시오. 16.2.6 또는 15.5.18로 고정하고, 끝난 뒤 캐시를 퍼지하십시오. 걱정해야 할 대상은 캡처로 화제가 된 CVE가 아니라 캐시된 모든 페이지에 영향을 미치는 Moderate 등급 취약점입니다.

Vercel에 호스팅해도 업그레이드해야 합니까?

그렇습니다. Vercel이 무력화하는 것은 셀프 호스팅 SSRF(CVE-2026-44578)뿐입니다. 미들웨어 우회, RSC 캐시 포이즈닝, DoS, XSS 권고는 Vercel에 호스팅된 App Router 앱에도 여전히 적용됩니다.

16.2.5 / 15.5.16이면 충분합니까, 아니면 16.2.6 / 15.5.18이 필요합니까?

16.2.6 / 15.5.18로 올려야 합니다. 앞선 빌드는 최초 13건을 수정했지만, 5월 7일 후속 권고(GHSA-26hh-7cqf-hhc6)에서 Turbopack 사용자의 segment-prefetch 우회가 다시 열린 것으로 확인됐습니다.

Next.js 14를 사용 중인데 패치는 어디에 있습니까?

패치는 없습니다. 13.x와 14.x에는 패치가 제공되지 않으며, 유일한 해결책은 15.x 또는 16.x로 마이그레이션하는 것입니다. 버전 업데이트가 아닌 마이그레이션으로 범위를 잡아야 합니다.

업그레이드 전까지 Cloudflare나 Vercel WAF 규칙으로 버틸 수 있습니까?

심층 방어 수단으로만 가능합니다. Cloudflare는 5월 6일 WAF와 어댑터 완화책을 배포했지만, Vercel은 아무 규칙도 내놓지 않았으며 패치만이 유일하게 완전한 해결책이라고 명시했습니다.

캐시가 앞단에 있는 RSC 사이트를 실제로 위협하는 권고는 무엇입니까?

RSC 캐시 포이즈닝 권고입니다. 엣지에서 s-maxage로 캐시된 오염 응답은 Cache-Tag를 퍼지할 때까지 모든 방문자에게 제공됩니다.

마지막 업데이트

2026년 9월 5일

카테고리Build

Google에서 이 사이트를 우선하기

Google 검색에서 omidsaffari.com을 선호 소스로 추가

omidsaffari.com을 선호 소스로 지정하면 Google이 Top Stories, AI Overviews, AI Mode에서 우선적으로 보여 줍니다.

Build의 다른 글

Build 글 전체 보기
뉴스레터

매주 일요일, 한 통의 편지. 뜨거운 의견이 아닌, 돌아가는 시스템.

AI 벤처 포트폴리오 운영에서 나오는 빌드 로그, 가동 중인 시스템, 현장 노트.

주간 발행. 스팸 없음. 언제든 해지 가능합니다.