기술 심층 분석: React Server Components가 어떻게 작동하고 취약점이 어디에 나타나는지
Source: Dev.to
간단하지만 정확한 RSC 요청 라이프사이클
-
들어오는 요청이 RSC 엔드포인트에 도달
- Next.js에서는 보통
/_rsc와 같은 내부 라우트에 매핑됩니다. - 요청은 인증 및 애플리케이션 미들웨어가 실행되기 전에 처리됩니다.
- Next.js에서는 보통
-
요청 본문 파싱 및 프로토콜 디코딩
- 페이로드는 React Flight 프로토콜에 따라 파싱됩니다.
- 여기에는 직렬화된 컴포넌트 레퍼런스, props, 실행 힌트의 디코딩이 포함됩니다.
- 이 단계에서 React는 페이로드 형태가 충분히 유효하다고 가정합니다.
-
컴포넌트 그래프 재구성
- React는 직렬화된 입력으로부터 서버 컴포넌트 트리를 재구성합니다.
- 모듈 레퍼런스를 해결하고 로드합니다.
- 비동기 경계와 Suspense 세그먼트를 등록합니다.
-
서버 렌더링 및 스트리밍
- 컴포넌트가 서버에서 실행됩니다.
- 출력이 다시 직렬화되어 청크 단위로 클라이언트에 스트리밍됩니다.
- 오류 경계와 부분 결과가 점진적으로 플러시됩니다.
-
오류 및 복구
- 스트림 중에 예외가 발생하면 React는 우아하게 복구하려 시도합니다.
- 메타데이터, 스택 트레이스, 모듈 식별자는 여전히 내부적으로 사용 가능합니다.
- 각 단계는 잘못된 형식이거나 악의적인 입력에 민감합니다.
서비스 거부(DoS) 공격이 발생하는 방식
DoS 문제는 주로 2단계와 3단계에서 발생하며, 애플리케이션 로직이 관여하기 전입니다.
악의적인 요청은 다음을 할 수 있습니다:
- 직렬화된 페이로드 크기를 부풀리기
- 깊게 중첩되거나 순환하는 레퍼런스 삽입
- 과도한 비동기 경계 해석 강제
개념적 예시 (JSON 페이로드):
POST /_rsc
Content-Type: application/json
{
"type": "ServerComponent",
"props": {
"children": {
"children": {
"children": {
"...": "..."
}
}
}
}
}
페이로드가 구문적으로 유효하더라도 React는:
- 이를 역직렬화하려 시도하고
- 메모리 내 컴포넌트 그래프를 구축하며
- 실행 및 스트리밍 작업을 스케줄링합니다.
그 결과:
- 디코딩 중 CPU 사용량 급증
- 그래프 재구성 시 메모리 압박
- 워커 또는 이벤트 루프 포화
핵심은 사용자 정의 코드에 도달하거나 인증을 거치지 않고도 발생한다는 점이며, 전통적인 애플리케이션 레이어 방어 가정이 크게 무효화됩니다.
소스 코드 노출이 발생할 수 있는 이유
소스 코드 노출은 4단계와 5단계에서 React가 오류를 처리하는 방식의 부수 효과입니다.
렌더링 또는 직렬화 오류가 스트림 중에 발생하면:
- React는 내부 모듈 경로에 대한 레퍼런스를 여전히 보유하고
- 스택 트레이스에 해결된 파일 위치가 포함되며
- 컴포넌트 메타데이터가 부분적으로 직렬화됩니다.
특정 엣지 케이스에서는 이 정보가:
- 스트리밍 응답에 누출되거나
- 오류 페이로드에 포함되거나
- 스트림이 정상적으로 중단되기 전에 플러시될 수 있습니다.
누출된 정보 예시 (JavaScript 오류):
Error: Failed to resolve Server Component
at /app/src/components/admin/UserList.server.tsx
at react-server-dom-webpack/server
이로 인해 다음이 드러납니다:
- 프로젝트 디렉터리 구조
- 서버 전용 컴포넌트 경계
- 내부 모듈 레이아웃 및 명명 규칙
단독으로는 익스플로잇이 아니지만, 타깃 공격을 위한 사전 작업 비용을 크게 낮춥니다.
서버 액션이 필요하지 않은 이유
많은 사람들이 이 위험이 서버 액션을 사용할 때만 발생한다고 오해합니다. 실제로는:
- 서버 액션은 API 표면일 뿐이며
- RSC는 런타임 및 프로토콜입니다.
다음과 같은 간단한 페이지라도:
export default async function Page() {
return ;
}
다음 과정을 거칩니다:
- 요청 역직렬화
- 컴포넌트 그래프 재구성
- 서버 실행 및 스트리밍
취약 로직은 서버 액션이 정의되어 있든 없든 실행됩니다.
주요 요점
- React Server Components는 요청 역직렬화, 컴포넌트 그래프 재구성, 스트리밍 기반 렌더링을 하나의 프레임워크 관리 파이프라인으로 결합하는 근본적으로 새로운 서버‑사이드 실행 모델을 도입합니다.
- 실제 공격 표면은 인증 이전, 애플리케이션 미들웨어 이전, 사용자 정의 비즈니스 로직 실행 이전에 존재합니다.
- 형식이 잘못되었거나 악의적인 RSC 요청은 다음을 초래할 수 있습니다:
- 서비스 거부 – 역직렬화 및 컴포넌트 그래프 재구성 중 과도한 CPU 사용, 메모리 압박, 워커·이벤트 루프 포화
- 서버‑사이드 구현 세부 정보의 우발적 노출 – 스트리밍 오류 처리 과정에서 내부 파일 경로, 컴포넌트 경계, 모듈 레이아웃 등이 노출
- 이러한 문제는 잘못 구성된 애플리케이션이나 안전하지 않은 사용자 코드 때문이 아니라 React Server Components 런타임 자체의 프레임워크 수준 취약점이며, 애플리케이션 레이어 방어만으로는 신뢰성 있게 완화할 수 없습니다.