본문으로 건너뛰기
개발 8분 읽기

SSR 데이터 노출 점검: 서버에서 만든 값이 브라우저로 새지 않게

Next.js와 SSR 환경에서 환경 변수, 사용자 세션, 캐시 데이터가 클라이언트로 노출되는 경로를 점검한다. SSR·Next.js·Data Leak 관점에서 기본 개념부터 구현 순서, 검증 방법, 문제가 생겼을 때 되돌리는 절차까지 설명한다.

박지민
에디터
2026년 6월 27일
SSR 데이터 노출 점검: 서버에서 만든 값이 브라우저로 새지 않게

핵심 요약

SSR 코드가 서버에서 실행된다는 사실만으로 데이터가 브라우저에 노출되지 않는 것은 아니다. 직렬화된 props, React Server Component 페이로드, 환경 변수 번들링, 사용자별 캐시 혼합, 오류 응답과 소스맵을 통해 값이 새어 나갈 수 있다. 데이터 경계를 코드·캐시·빌드 산출물·브라우저 테스트로 검증해야 한다.

Next.js 같은 SSR 프레임워크는 서버와 클라이언트 코드를 한 저장소에서 다루기 때문에 경계가 편리한 만큼 흐려지기 쉽다. 개발자는 서버에서 조회한 객체를 그대로 컴포넌트에 전달하고, 화면에 표시하지 않았으니 안전하다고 생각할 수 있다. 하지만 브라우저로 직렬화된 데이터는 개발자 도구, 네트워크 응답, RSC 페이로드 또는 초기 HTML에서 확인될 수 있다.

또 다른 위험은 사용자별 데이터가 캐시 키에 충분히 반영되지 않아 다른 사용자에게 재사용되는 경우다. 이 문제는 비밀값 하나의 노출보다 영향 범위가 크고, 정상 응답처럼 보이기 때문에 탐지가 늦다. 프레임워크 기능과 기본값은 버전별로 달라지므로 배포 전 현재 Next.js 공식 데이터 보안·캐시 문서를 다시 확인해야 한다.

브라우저로 넘어가는 경로부터 목록화한다

경로대표 위험검증 방법
Client Component props서버 객체 전체 직렬화네트워크·RSC 페이로드와 타입 검토
초기 HTML·hydration 데이터세션·내부 ID 포함페이지 소스와 응답 본문 검색
NEXT_PUBLIC_next.config.js env빌드 시 클라이언트 번들 포함빌드 산출물 문자열 스캔
Route Handler/API 응답과도한 필드·오류 세부정보계약 테스트와 응답 스키마 검증
공유 캐시·CDN사용자 A 응답을 B에게 제공서로 다른 계정의 교차 요청 테스트
오류·로그·소스맵토큰, 쿼리, 내부 경로 노출프로덕션 오류 응답과 공개 파일 확인
프리패치·링크 미리보기사용자가 열지 않은 데이터 요청브라우저 네트워크 기록 확인

서버 전용 객체를 UI 모델로 줄인다

가장 안전한 패턴은 데이터베이스나 외부 API 응답을 그대로 넘기지 않고, 화면에 필요한 필드만 새 객체로 만든 뒤 클라이언트 경계에 전달하는 것이다.

// 나쁜 예: 내부 필드와 토큰까지 포함된 객체를 그대로 전달
const account = await db.account.findUnique({ where: { id: session.userId } })
return <AccountCard account={account} />

// 더 안전한 예: 화면에 필요한 값만 명시적으로 선택
const account = await db.account.findUnique({
  where: { id: session.userId },
  select: { displayName: true, plan: true, renewalDate: true },
})

return <AccountCard account={account} />

TypeScript 타입만 줄이는 것으로는 충분하지 않다. 타입 단언은 런타임 객체의 필드를 제거하지 않기 때문이다. 데이터 조회 단계에서 select를 사용하거나 명시적인 DTO를 생성해 실제 객체를 축소한다.

환경 변수는 이름이 아니라 번들 경로로 확인한다

Next.js는 기본적으로 서버 환경 변수를 서버에서 사용하지만, NEXT_PUBLIC_ 접두사가 붙은 값은 클라이언트에 노출될 수 있다. 또한 next.config.jsenv 설정에 넣은 값은 번들에 포함될 수 있으므로 비밀값을 두면 안 된다.

배포 전 다음을 자동화한다.

  • 저장소에서 NEXT_PUBLIC_, next.config.*env, 클라이언트 컴포넌트의 process.env 사용을 검색한다.
  • 빌드 산출물에서 알려진 비밀값의 테스트용 canary 문자열을 찾는다.
  • 클라이언트 번들에 내부 API 호스트, 관리자 경로, 비공개 기능 플래그가 들어가는지 확인한다.
  • 비밀값은 서버 전용 모듈에서만 읽고, 클라이언트가 필요한 공개 설정은 별도 스키마로 관리한다.

비밀을 sensitive라는 이름의 변수에 넣거나 난독화하는 것은 보호가 아니다. 브라우저가 실행에 필요로 하는 값은 결국 사용자가 읽을 수 있다고 가정해야 한다.

사용자별 캐시 혼합을 막는다

SSR 데이터 유출의 가장 위험한 실패 모드는 인증은 맞지만 캐시가 틀린 경우다.

  1. 사용자·테넌트별 응답을 전역 캐시에 저장하지 않는다.
  2. 캐시 키에 사용자 ID를 넣는 것만으로 끝내지 말고 권한 변경과 로그아웃 시 무효화 조건을 정의한다.
  3. Cookie, Authorization, 테넌트 헤더가 있는 응답의 CDN 캐시 정책을 점검한다.
  4. 공개 데이터와 개인 데이터를 같은 함수에서 조합할 때 캐시 경계를 분리한다.
  5. 두 계정으로 같은 URL을 번갈아 호출해 응답 교차 여부를 E2E 테스트한다.

테스트는 관리자와 일반 사용자, 서로 다른 테넌트, 로그아웃 직후, 권한 변경 직후를 포함해야 한다. 화면 캡처만 비교하지 말고 원시 응답과 RSC 네트워크 페이로드를 비교한다.

인증과 인가는 데이터 접근 지점에서 반복한다

페이지 레이아웃에서 로그인 여부를 확인했더라도 Server Action, Route Handler, 데이터 접근 함수에서 다시 권한을 확인해야 한다. UI에서 버튼을 숨기는 것은 인가가 아니다.

  • 세션 존재 여부와 대상 자원 소유권을 분리해 검사한다.
  • Server Action 입력을 신뢰하지 않고 서버에서 스키마 검증한다.
  • 테넌트 ID를 클라이언트가 보낸 값만으로 결정하지 않는다.
  • 관리자 역할은 데이터 조회 직전 검증하고 감사 로그를 남긴다.
  • 오류 메시지에는 자원 존재 여부나 내부 쿼리를 노출하지 않는다.

탐지 신호와 회귀 테스트

신호의심할 문제
서로 다른 사용자가 동일 응답 해시를 받음사용자별 캐시 키 누락
로그아웃 후 개인 데이터가 다시 보임브라우저·CDN 캐시 또는 세션 무효화 실패
클라이언트 번들에서 토큰 패턴 발견환경 변수 또는 설정 번들링
오류 응답 크기가 갑자기 증가스택·객체 전체 직렬화
RSC 페이로드에 내부 필드가 등장서버 객체를 클라이언트 props로 전달
특정 리전·CDN에서만 교차 노출캐시 정책 배포 불일치

CI에서는 테스트 계정 A와 B를 만들고 동일 경로를 반복 요청한 뒤, 각 계정의 고유 canary 값이 상대 응답에 나타나지 않는지 검사할 수 있다. 이 테스트는 실제 캐시·프록시를 통과하는 스테이징 환경에서도 실행해야 한다.

노출이 의심될 때 대응 순서

  1. 영향을 받은 경로의 캐시와 프리렌더 산출물을 무효화한다.
  2. 노출 가능성이 있는 토큰·세션·API 키를 범위에 따라 회전한다.
  3. 원시 응답, RSC 페이로드, CDN 로그, 배포 버전을 보존한다.
  4. 어떤 사용자 데이터가 어떤 캐시 키로 섞였는지 영향 범위를 재현한다.
  5. 사고 커뮤니케이션 절차에 따라 확인된 사실과 조사 범위를 분리한다.
  6. 로그에 개인정보가 추가로 남았는지 관측성 데이터 점검을 수행한다.

운영 체크리스트

  • Client Component에 전달되는 props를 필드 단위로 검토한다.
  • 데이터베이스 조회 단계에서 필요한 필드만 선택한다.
  • NEXT_PUBLIC_next.config.js env에 비밀값이 없다.
  • 빌드 산출물과 공개 소스맵에서 canary 비밀을 검색한다.
  • 개인 응답의 CDN·서버 캐시 정책을 명시한다.
  • 두 사용자·두 테넌트 간 교차 응답 E2E 테스트가 있다.
  • Server Action과 Route Handler에서 인가를 다시 수행한다.
  • 오류 응답과 로그에서 토큰·세션·내부 객체를 제거한다.
  • 프레임워크 업그레이드 후 데이터 경계 회귀 테스트를 실행한다.

함께 읽을 글

API 응답 스키마와 오래된 경로는 API 버전 폐기 보안에서, 로그와 트레이스의 민감정보는 관측성 데이터 개인정보에서 이어서 점검할 수 있다.

참고 기준

결론

SSR 보안의 기준은 코드가 서버에서 실행되는지가 아니다. 어떤 값이 직렬화되고, 어떤 응답이 캐시되며, 브라우저가 실제로 무엇을 받는지를 검증해야 한다. 화면에 보이지 않는 필드까지 원시 응답과 빌드 산출물에서 검사하는 습관이 사용자 간 데이터 노출을 막는다.

전체 댓글 0

댓글을 불러오는 중입니다...
새로고침

태그

SSR Next.js Data Leak

공유하기

관련 기사