React RCE 취약점(CVE-2025-55182) – 문제와 발생 원인
2025년 12월 초 공개된 React Server Components 취약점(CVE-2025-55182, 일명 React2Shell / React4Shell)은 CVSS 10.0이 매겨진, 사전 인증 없이 원격 코드 실행(RCE)이 가능한 매우 심각한 이슈다. 이미 공개 PoC와 실제 스캔·공격 시도가 관측되고 있다.
이 글은 “누가, 왜, 어디까지 위험한지”를 조금 더 체계적으로 정리한 버전이다.
한눈에 보는 요약
-
문제의 핵심 React Server Components(RSC)가 사용하는 Flight 프로토콜 역직렬화 로직의 결함 때문에, 공격자가 조작한 요청을 보내면 서버에서 임의 JS 코드가 실행될 수 있다.
-
영향 받는 주요 구성요소
-
react-server-dom-webpack / -parcel / -turbopack19.0 / 19.1.0 / 19.1.1 / 19.2.0 -
이들을 내장한 Next.js 15.x, 16.x, 14.3.0-canary.77 이후 버전(App Router 사용 시)
-
브라우저에서만 도는 “전통적 React SPA” RSC/Server Actions/서버 렌더링을 전혀 사용하지 않는 순수 클라이언트 React 앱은 영향 없음.
-
** 독자 중에 과거에 Next.js를 사용했다면 당장 해야 할 일**
- 프로젝트에서
react-server-dom-*/next버전 확인 - React는 19.0.1 / 19.1.2 / 19.2.1 이상으로 업데이트
- Next.js는 공지된 패치 버전(15.0.5, 15.1.9, 15.2.6, 15.3.6, 15.4.8, 15.5.7, 16.0.7 등)으로 업데이트 또는 안정 버전으로 다운그레이드
- 이미 인터넷에 노출된 서버라면, 로그·WAF 룰·이상 징후를 함께 점검

이번에 발견된 취약점은 무엇인가
React 팀이 공개한 내용만 정리하면, 이번 취약점은 다음과 같이 요약된다.
- 취약 대상: React Server Components(RSC) 구현체(
react-server-dom-*) - 취약 구간: 클라이언트 → 서버로 RSC 데이터를 전송할 때 사용하는 Flight 프로토콜 디코딩(역직렬화) 로직
-
공격 방법:
-
공격자가 특수하게 조작된 Flight 페이로드를 RSC/Server Actions HTTP 엔드포인트로 전송
- 서버가 이 데이터를 “신뢰”하고 역직렬화하는 과정에서, 내부 모듈 로딩/객체 생성 로직이 공격자 입력에 끌려다님
- 그 결과, 서버 환경에서 임의 코드 실행(RCE) 가능
중요한 점은, 로그인/세션 없이도(Unauthenticated) 공격이 가능하다는 것이다. 외부에 노출된 Next.js RSC 서버라면 “인터넷에 열려 있는 순간부터” 스캔·공격 대상이 된다.
취약점이 발생하는 정확한 조건
1. React/Next.js 버전 조건
-
React RSC 관련 패키지
-
react-server-dom-webpack react-server-dom-parcel-
react-server-dom-turbopack -
취약 버전
-
19.0, 19.1.0, 19.1.1, 19.2.0
-
패치 버전
-
19.0.1, 19.1.2, 19.2.1
-
Next.js (App Router 기반)
-
취약:
- 14.3.0-canary.77 이상
- 15.x, 16.x (RSC를 포함하는 기본 App Router 구조)
-
패치:
-
15.0.5, 15.1.9, 15.2.6, 15.3.6, 15.4.8, 15.5.7, 16.0.7 등
2. 아키텍처/런타임 조건
다음 조건이 모두 맞을 때 실제로 공격 가능성이 높다.
-
RSC 사용 * Next.js App Router, 또는 다른 RSC 지원 프레임워크 사용
-
서버에서 React 코드 실행 * Node.js 등에서 React Server Components/Server Actions가 돌아가는 구조
-
HTTP로 외부에 노출된 서버 * 인터넷 또는 내부망에서라도 공격자가 HTTP 요청을 보낼 수 있는 환경
반대로, 다음과 같은 환경은 이번 취약점과 거의 무관하다.
- CRA/Vite 기반 순수 클라이언트 React SPA
- RSC/Server Actions를 전혀 쓰지 않고, 단순 정적 번들만 S3, CDN 등에 올리는 방식
- “React는 오직 브라우저에서만” 실행되고, 서버는 완전히 별개의 기술 스택인 구조(예: React + Django REST, React + FastAPI)
React-DRF, React-FastAPI 조합은 왜 상대적으로 안전했는가
React를 Django REST Framework 또는 FastAPI 같은 파이썬 백엔드와 조합해 사용하는 구조에서는 문제가 발생하지 않는다. 이유는 다음과 같다.
- React 코드가 서버에서 실행되지 않는다
- React는 브라우저에서만 돌고, Django/FastAPI는 순수 HTTP API 서버 역할만 수행
- 서버는 React의 Flight 프로토콜을 이해할 필요도, RSC를 해석할 필요도 없다.
- 통신 경계가 명확하다
- React ↔ 서버 간 통신은 JSON/HTTP API로 명확히 구분되고,
- 서버는 “React 내부 모듈”을
require()하거나, 클라이언트가 넘긴metadata값으로 모듈을 로딩하지 않는다.
- RSC·Server Actions·Flight 프로토콜을 사용하지 않는다
- 이번 취약점의 공격 표면은 “RSC Flight 페이로드를 해석하는 서버 쪽 React 코드”에 있다.
- Django/FastAPI는 해당 코드를 아예 포함하지 않는다.
결국, 이번 사고는 “React + 서버 통합 프레임워크(Next.js 등)에서만 발생할 수 있는 구조적 문제”였다. 역할이 분리된 React-SPA + REST API 조합은 애초에 문제가 되는 코드 경로가 존재하지 않았다.
Flight 프로토콜과 서버 측 프로토타입 오염
조금 더 기술적으로 들어가 보면, 취약점은 Flight 프로토콜 역직렬화 → 프로토타입 오염 → RCE라는 전형적인 흐름을 가진다.
1. 문제가 된 코드 패턴
문제가 된 함수의 개념적인 형태는 다음과 같다(개념화한 예).
// 취약 버전 (개념 코드)
export function requireModule<T>(metadata: ClientReference<T>): T {
const moduleExports = parcelRequire(metadata[ID]); // 모듈 로딩
return moduleExports[metadata[NAME]]; // 클라이언트 입력을 그대로 사용
}
metadata[ID]: 어떤 모듈을 로딩할지metadata[NAME]: 그 모듈의 어떤 export에 접근할지
여기서 metadata[NAME]이 전혀 검증 없이 그대로 사용된 것이 문제다.
공격자가 이 값을 __proto__, constructor 같은 민감한 키로 바꾸면, 모듈 export 대신 객체 프로토타입 체인에 손을 댈 수 있는 길이 열린다.
2. 프로토타입 오염 → RCE
-
서버 코드 어딘가에서 “평범한 객체 생성”을 할 때,
-
이미 오염된
Object.prototype의 속성들이 따라붙는다. - 그 안에
spawnSync('sh')같은 셸 실행 로직이 연결되어 있으면, 새로 생성되는 객체를 경유해 코드가 실행될 수 있다. - 이렇게 해서 공격자는 단 한 번의 HTTP 요청으로도 원격 코드 실행에 도달할 수 있다.
실제로 무엇을 패치했는가
React 측 패치의 핵심은 “클라이언트가 보낸 값을 그대로 믿지 않도록 강제”하는 것이다.
대표적인 변경이 바로 requireModule 함수다.
// 패치 후 (개념 코드)
export function requireModule<T>(metadata: ClientReference<T>): T {
const moduleExports = parcelRequire(metadata[ID]);
// 👇 클라이언트가 보낸 NAME이 실제 own property인지 확인
if (hasOwnProperty.call(moduleExports, metadata[NAME])) {
return moduleExports[metadata[NAME]];
}
return undefined as any;
}
이 한 줄로 인해:
__proto__,constructor등 프로토타입 체인을 건드리는 키는 own property가 아니기 때문에 접근이 차단된다.- 결과적으로, 프로토타입 오염을 RCE로 연결하는 핵심 공격 벡터가 막힌다.
실제 패치에는 이외에도:
- Flight 페이로드 구조 검증 강화
- 특정 위험한 필드의 사용 제한
- 관련 코드 경로에 대한 추가 방어 로직
등이 포함된 것으로 알려져 있다. 하지만 큰 그림에서 보면 위와 같은 “검증 추가”가 핵심이다.
Next.js가 React를 서버로 확장하면서 생긴 구조적 충돌
원래 React는 “브라우저에서 돌아가는 UI 라이브러리”였다. 그러나 RSC와 Server Actions가 등장하면서, React는 더 이상 브라우저 전용이 아니다.
Next.js는 이 기능들을 통합하여:
- 클라이언트 컴포넌트 + 서버 컴포넌트 + 서버 액션을 하나의 앱처럼 보이게 만들었다.
- 그 대가로, 클라이언트가 보낸 데이터가 서버 모듈 로딩/코드 실행 경로를 직접 건드릴 수 있는 구조가 되었다.
여기서 보안 모델이 어긋났다.
프론트엔드 가정을 서버로 가져온 문제
프론트엔드 환경에서는 보통:
- 코드 번들이 정적이고 불변이며,
- 모듈 로딩은 빌드 시점에 결정되어 있고,
- 런타임에 사용자가 모듈 이름이나 export 이름을 바꾸는 일은 거의 없다.
그래서,
moduleExports[userInput]
같은 패턴이 상대적으로 덜 위험하게 느껴질 수 있다.
하지만 서버에서는 이야기가 완전히 다르다.
- 입력은 항상 신뢰할 수 없는 네트워크 데이터이고,
- 모듈 로딩/객체 생성은 곧 서버 자원·파일·프로세스 접근과 직결된다.
- 이런 환경에서 역직렬화/다이내믹 로딩에 사용자 입력을 섞으면, 항상 RCE 후보가 된다.
필요했던 것은 다음과 같은 “새로운 보안 모델”이었다.
- Flight 페이로드 구조에 대한 엄격한 스키마 검증
- 클라이언트가 제어 가능한 값에 대한 화이트리스트 기반 접근
- HMAC 같은 요청 서명/무결성 검증
- 서버 측에서 “RSC는 항상 신뢰할 수 없는 입력을 받는다”는 가정 하에 설계
그러나 실제 구현에서는 전통적인 React(프론트 전용) 신뢰 모델이 서버 영역까지 그대로 끌려와, 이번과 같은 구조적 취약점이 드러났다.
결론: 프론트엔드와 서버 경계가 흐려질수록, 보안 모델부터 다시 설계해야 한다
이번 React/Next.js 취약점은 단순한 “버그 하나”라기보다는:
- 프론트엔드 라이브러리였던 React가 서버 실행 경로까지 포괄하는 순간,
- 기존의 “프론트엔드 중심 신뢰 모델”이 더 이상 통하지 않게 되었다는 것을 보여준다.
전통적인 React + DRF/FastAPI처럼:
- “브라우저에서만 React가 돌고”
- “서버는 HTTP API만 제공하는” 아키텍처에서는 이번과 같은 취약점이 설 자리가 없다.
반대로, Next.js처럼:
- 서버 실행 경로에 React 직렬화/역직렬화 프로토콜이 깊게 엮인 구조에서는
- 기능과 DX 향상만큼이나 새로운 보안 모델과 검증 체계를 함께 설계해야 한다.
앞으로도 “프론트엔드-백엔드 경계가 사라지는” 프레임워크는 계속 나올 것이다. 이번 사건은 그럴수록 편의성 못지않게, 보안 경계와 신뢰 모델을 제일 먼저 설계해야 한다는 경고라고 볼 수 있다.
댓글이 없습니다.