React (RSC) 취약점이 실제로 존재하고 엔지니어링 팀에 피해를 주고 있다

발행: (2026년 1월 9일 오전 01:11 GMT+9)
16 min read
원문: Dev.to

Source: Dev.to

React (RSC) Exploits가 실제이며 엔지니어링 팀에 피해를 주는 커버 이미지

Introduction

웹 엔지니어링 분야에 종사한다면, 2026년으로의 전환은 새로운 기능들에 의해 정의된 것이 아니라, 단 하나의 무시무시한 깨달음에 의해 정의되었습니다: 클라이언트와 서버 사이의 경계가 무너졌습니다.

지난 12월, React2Shell (CVE‑2025‑55182)이 React Server Components에서 치명적인 RCE를 드러냈습니다. 팀들이 패치를 적용하자마자, 같은 서브시스템에서 두 개의 고위험 악용 사례가 추가로 등장했습니다. 이것이 바로 결정적인 기술 사후 분석입니다.

“React2Shell” RCE (CVE‑2025‑55182)

이 취약점(CVSS 10.0)이 왜 이렇게 파괴적이었는지 이해하려면, 먼저 파괴된 React Flight Protocol을 이해해야 합니다.

수년간 React 개발자들의 사고 모델은 단순했습니다: React는 브라우저에서 UI를 렌더링하고, API(REST/GraphQL)가 데이터를 가져옵니다. 서버와 클라이언트는 명확한 네트워크 경계로 구분된 별개의 세계였습니다.

React Server Components(RSC)는 그 경계를 없앴습니다.

React Flight 프로토콜 다이어그램

RSC 세계에서는 서버가 클라이언트에 HTML을 보내지 않습니다. 대신 Flight라는 독점 직렬화 형식을 스트리밍합니다. 이 스트림에는 다음이 포함됩니다:

  • UI 컴포넌트에 대한 설명
  • 직렬화된 데이터
  • Promises(데이터로 해결되는)

React2Shell 작동 방식

React2Shell 은 표준 SQL‑인젝션이나 XSS가 아니었습니다. 직렬화 해제기를 대상으로 하는 논리 남용이었습니다. 공격자는 특정 Flight 페이로드를 포함한 HTTP POST 요청을 만들었습니다. 이러한 바이너리와 유사한 스트림은 유효한 컴포넌트 트리를 흉내 내면서 Thenable 객체(가짜 Promise)를 숨겼습니다.

단계설명
주입공격자는 악의적인 __proto__ 키를 포함한 직렬화된 객체를 전송합니다.
역직렬화서버가 스트림을 파싱하고 가짜 Promise를 해결하려 시도합니다.
프로토타입 오염정화가 부족해 __proto__가 실행 중인 Node.js 프로세스의 Object.prototype을 덮어쓸 수 있습니다.
가젯 체인수정된 프로토타입이 내부 호출을 트리거하여 Function 생성자에 도달합니다.
실행Function에 대한 접근으로 require('child_process').exec(...)와 같은 임의의 코드를 즉시 컴파일하고 실행할 수 있습니다.

Next.js에 미친 영향

Next.js App Router가 기본적으로 RSC를 활성화하기 때문에, 모든 Next.js 애플리케이션(버전 15.x, 16.x 및 카나리 빌드)은 처음부터 취약했습니다. 해킹당하기 위해 버그가 있는 Server Action을 작성할 필요도 없었고, Server Components를 명시적으로 사용할 필요도 없었습니다. 애플리케이션이 react-server-dom-webpack을 import했다면, 해당 Flight 페이로드를 수신 대기하고 있었습니다.

Client‑server component interaction diagram

크레딧: 이미지 제공: Next.js

DevOps 악몽

WAF 맹목

표준 웹 애플리케이션 방화벽(WAF)은 ' OR 1=1 같은 고전적인 SQL 인젝션 공격이나 <script> 같은 스크립트 태그를 찾도록 훈련되어 있습니다. 그러나 React Flight의 독점 텍스트 형식을 검사하도록 훈련되지 않았기 때문에, 악성 페이로드가 처음에는 Cloudflare와 AWS WAF 규칙을 그대로 통과했습니다.

공급망 지옥

취약점은 node_modules 깊숙이 숨어 있었습니다. 코드를 단순히 “수정”할 수 없었습니다. Vercel과 React 팀이 패치된 바이너리를 릴리스할 때까지 기다린 뒤, 모든 마이크로서비스를 다시 빌드하고 재배포해야 했습니다.

여진 (DoS 및 누출)

엔지니어링 팀이 RCE 패닉에서 회복하고 있던 시점에, 보안 커뮤니티는 기반 구조에 더 많은 균열을 발견했습니다. 2025년 12월 11일, React 팀은 세 개의 추가 CVE에 대한 새로운 권고를 발표했습니다.

이것들은 RCE는 아니지만 운영상 파괴적이며 현재 직렬화 구현의 취약성을 강조합니다.

1. 서비스 거부 (DoS) (CVE‑2025‑55184)

심각도: 높음 (7.5/10)

이 취약점을 이용하면 공격자가 단일 요청만으로 인프라를 “동결”시킬 수 있습니다.

메커니즘

Flight 프로토콜은 데이터 청크가 다른 청크를 참조하도록 허용합니다. 연구원들은 페이로드에 순환 종속성을 만들 수 있음을 발견했습니다:

  1. 청크 A가 청크 B를 참조합니다.
  2. 청크 B가 청크 A를 참조합니다.

React 디시리얼라이저가 이 구조를 해석하려 할 때 무한 동기 루프에 빠집니다.

Node.js event‑loop blockage illustration

인프라에 대한 영향

Node.js는 단일 스레드이기 때문에 이 무한 루프는 이벤트 루프를 차단하고 전체 프로세스를 응답 불가능하게 만듭니다. 일반적인 Next.js 배포 환경에서는 하나의 손상된 요청만으로 전체 서버나 컨테이너가 다운될 수 있어, 전체 플릿에 걸친 서비스 거부가 발생합니다.

2. 소스 코드 노출 (CVE‑2025‑55185)

심각도: 중간 (6.2/10)

Flight 디시리얼라이저의 결함으로 공격자가 내부 모듈 식별자를 요청할 수 있었으며, 이는 오류 메시지에 반영되었습니다. 이와 프로토타입 오염 버그를 연계하면 공격자는 서버‑사이드 소스 코드 조각을 가져올 수 있어 비즈니스 로직 및 잠재적인 API 키가 유출됩니다.

3. 인증되지 않은 서버‑액션 호출 (CVE‑2025‑55186)

심각도: 높음 (7.8/10)

Next.js 15에 도입된 서버 액션은 클라이언트에서 CSRF 토큰과 함께 호출되어야 합니다. 그러나 요청 본문에 특수하게 조작된 Flight 페이로드가 포함될 경우 토큰 검사를 우회할 수 있는 취약점이 발견되어, 인증되지 않은 사용자가 임의의 서버‑사이드 로직(예: 데이터베이스 쓰기, 이메일 전송)을 트리거할 수 있습니다.

교훈 및 완화 방안

AreaRecommendation
패치 관리React와 Next.js를 버전 ≥ 16.0.3(또는 최신 보안 패치가 적용된 릴리스)으로 고정하십시오. 승인된 바이너리를 강제하기 위해 내부 미러를 사용하십시오.
WAF 규칙WAF 서명을 확장하여 바이너리 Flight 페이로드를 검사하도록 하십시오. 비정상적으로 큰 __proto__ 필드나 반복되는 청크 참조를 찾아보세요.
런타임 강화Function 생성자를 프로덕션에서 비활성화하십시오 (--disable-features=FunctionConstructor). 동적 코드 실행을 위해 vm 샌드박스를 사용하십시오.
가시성Flight 역직렬화 지연 시간 및 청크‑참조 깊이에 대한 메트릭을 추가하십시오. DoS 시도를 나타낼 수 있는 급증에 대해 알림을 설정하십시오.
공급망 감사각 종속성 업그레이드 후 npm audityarn audit을 실행하십시오. React‑특화 공격 표면을 이해하는 Snyk와 같은 도구 사용을 고려하십시오.
제로 트러스트 배포각 Next.js 서비스를 사이드카 뒤에 배치하여 들어오는 Flight 스트림을 스키마에 맞게 검증한 후 Node.js 프로세스로 전달하도록 하십시오.

마무리 생각

React2Shell 사건은 클라이언트‑서버 경계가 더 이상 안전한 추상화가 아니라는 것을 증명했습니다—공격자가 무기로 사용할 수 있는 가변적인 표면입니다. 2026년이 깊어갈수록 팀은 직렬화 형식(예: Flight)을 REST/GraphQL API에 적용하는 것과 동일한 엄격함으로 다루어야 합니다: 엄격한 스키마 검증, 철저한 퍼징 테스트, 지속적인 모니터링.

경계를 늦추지 말고, 종속성을 최신 상태로 유지하며, “서버 전용” 코드가 자동으로 안전하다고 가정하지 마십시오.

1. 서비스 거부 (CVE‑2025‑67779)

Severity: 치명적 (9.8/10)

Impact:

  • CPU 급증: CPU가 즉시 100 %까지 올라갑니다.
  • 요청 손실: 서버가 다른 모든 사용자에 대한 응답을 중단합니다. 헬스 체크가 실패합니다.
  • 클러스터 불안정화: Kubernetes에서 liveness probe가 실패하여 파드가 재시작될 수 있습니다. 공격자가 이러한 요청을 지속적으로 스트리밍하면 전체 클러스터가 CrashLoopBackOff 상태에 빠져 애플리케이션이 사실상 다운됩니다.

Note: 초기 수정이 불완전했으며, 이는 CVE‑2025‑67779(“수정에 대한 수정”)을 초래했습니다. 이로 인해 DevOps 팀은 2주 안에 세 번째 패치를 적용해야 했습니다.

2. Source‑Code Leak (CVE‑2025‑55183)

Severity: Medium (5.3/10)

Why it matters:
이는 React 생태계에서 가장 당혹스러운 취약점이라고 할 수 있습니다. 공격자가 서버를 속여 소스 코드를 클라이언트에게 다시 전송하도록 만들 수 있습니다.

Mechanism

이 익스플로잇은 JavaScript 문자열 강제 변환에 의존합니다. 서버 액션이 서버 런타임 내에서 함수에 대해 암묵적으로 .toString()을 호출하는 객체를 반환하면, V8의 기본 동작이 해당 함수의 소스 코드를 반환합니다.

“Secrets” Risk

개발자들은 use server 파일 안의 코드는 비공개라고 가정하는 경우가 많습니다. 예를 들어 다음과 같이 작성할 수 있습니다:

// DO NOT DO THIS
const STRIPE_KEY = "sk_live_12345";
export async function purchase() { /* … */ }

보통 상황에서는 STRIPE_KEY가 서버에만 남아 있습니다. 하지만 CVE‑2025‑55183이 존재하면, 공격자는 반환값의 직렬화를 조작해 함수의 스코프를 덤프함으로써 하드코딩된 API 키, 내부 주석, 데이터베이스 스키마 상세 정보 등을 노출시킬 수 있습니다.

즉각적인 실행 계획

Next.js App Router (v13.3+) 또는 React 19를 사용 중이라면, 아래 단계를 즉시 수행하십시오.

“즉각적인” 업데이트

시맨틱 버전 범위에 의존하지 마세요. 논의된 모든 CVE를 명시적으로 해결한 릴리스로 의존성을 고정하십시오.

npm install react@19.0.2 react-dom@19.0.2 next@15.1.3
# 깊게 중첩된 의존성 확인
npm list react-server-dom-webpack

react-server-dom-webpack 버전이 최소 19.0.2인지 확인하십시오.

레이트 리밋 적용

DoS 공격은 공격자에게 비용이 저렴합니다. 모든 라우트—특히 POST 요청—에 대해 인프라 수준(Nginx, Cloudflare, AWS WAF 등)에서 적극적인 레이트 리밋을 적용하십시오.

비밀 정보 감사

서버 코드가 누출될 수 있다고 가정하십시오. app/ 디렉터리를 스캔하십시오. .ts 또는 .js 파일에서 API 키, 하드코딩된 비밀번호, 내부 IP 주소와 같은 비밀 정보를 발견하면 즉시 환경 변수로 옮기십시오.

결론

React Server Components는 웹의 강력한 진화이지만, **“React2Shell”**은 이 힘이 무시무시한 새로운 공격 표면을 만든다는 것을 보여주었습니다. Flight 프로토콜은 복잡하고, 우리가 보았듯이 복잡성은 보안의 적입니다.

읽어 주셔서 감사합니다! 이 글이 도움이 되었다면, 이 글을 필요로 할 수 있는 다른 사람들과 공유해 주세요. 다른 블로그 글도 자유롭게 탐색하고 소셜 미디어에서 저를 팔로우해 주세요.

더 읽기

Back to Blog

관련 글

더 보기 »