Blog

Next.js에서 보안 헤더를 설정한다는 것의 의미와 한계


서론#

얼마 전 개인 블로그를 Security Headers를 통해 분석해보니,
보안 점수가 빨간불로 표시된 꽤 처참한 결과를 확인했다.

특별히 위험한 기능을 구현한 것도 아니고,
인증이 필요한 서비스도 아닌 단순한 블로그였지만
기본적인 보안 헤더조차 설정되어 있지 않았다는 점이 눈에 띄었다.

이 경험을 계기로,
“Next.js를 사용하면 보안은 어디까지 신경 써야 하는가?”
라는 질문을 다시 정리해보게 되었다.

왜 보안 헤더를 직접 설정해야 하는가?#

Next.js는 보안에 무관심한 프레임워크가 아니다.
다만, 보안과 관련된 많은 선택을 기본값으로 강제하지 않을 뿐이다.

이는 보안을 포기해서가 아니라,
서비스의 성격과 요구 사항에 따라 적용해야 할 정책이 다르기 때문이다.

즉, Next.js는
보안을 “자동으로 완성해주는 도구”라기보다는
어디까지를 프레임워크가 맡고,
어디부터를 개발자가 책임져야 하는지
경계를 명확히 드러내는 구조에 가깝다.

이 글에서 다루는 보안 옵션의 범위#

이 글에서는 Next.js에서 설정할 수 있는
대표적인 보안 헤더 몇 가지를 기준으로,
그 역할과 한계를 정리한다.

다만 이 글의 목적은
공격 기법을 상세히 설명하거나,
보안을 완성하는 방법을 제시하는 데 있지 않다.

대신 다음 질문에 집중한다.

  • 이 보안 옵션들은 무엇을 막아주는가
  • 그리고 무엇은 막아주지 않는가

Next.js에서 설정한 보안 헤더 예시#

아래는 개인 블로그에 적용한 보안 헤더 설정이다.

const securityHeaders = [
  { key: "X-Frame-Options", value: "SAMEORIGIN" },
  { key: "X-Content-Type-Options", value: "nosniff" },
  { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
  {
    key: "Permissions-Policy",
    value: "camera=(), microphone=(), geolocation=()",
  },
];

// 이 헤더들은 Next.js의 `next.config.js`에서 `headers()` 옵션을 통해 설정할 수 있다.

이 설정이 정답이거나,
모든 서비스에 그대로 적용해야 하는 기준은 아니다.

다만 Next.js 기준에서
“최소한 고려해볼 수 있는 보안 헤더”로는
무리가 없는 구성이다.

이제 각 보안 헤더가
무엇을 해주는지보다,
무엇을 해주지 않는지를 먼저 살펴보자.

각 보안 헤더는 무엇을 "막아주지 않는가"#

보안 헤더를 설명할 때 가장 흔한 실수는
“이 헤더를 쓰면 @@ 공격을 막을 수 있다”라고 단정하는 것이다.

이 섹션에서는 각 보안 헤더가 무엇을 막아주는지보다,
무엇을 책임지지 않는지를 기준으로 살펴본다.

4-1. X-Frame-Options#

X-Frame-Options는 페이지가 iframe 안에서 렌더링되는 방식을 제한한다.

이 설정을 통해,
다른 출처에서 내 페이지를 감싸는 형태의 UI 공격을 줄일 수 있다.

하지만 이 헤더는 다음을 보장하지 않는다.

  • 인증을 보호해주지 않는다
  • 권한 검증을 대신해주지 않는다
  • 서버 요청 자체를 차단하지 않는다

즉, 이 옵션은
“누가 이 페이지를 볼 수 있는가”를 결정하지 않는다.

단지,
어떤 방식으로 화면이 노출될 수 있는가를 제한할 뿐이다.

참고로, X-Frame-Options는 최신 브라우저에서는
Content-Security-Policy: frame-ancestors로 대체되는 흐름에 있으며,
레거시 대응 목적이 아니라면 CSP 사용이 더 권장된다.

4-2. X-Content-Type-Options#

X-Content-Type-Options: nosniff는
브라우저가 파일의 실제 내용을 추측해
임의로 실행하는 동작을 막는다.

이 설정은 브라우저의 위험한 해석을 줄여주지만,
다음과 같은 역할을 하지는 않는다.

  • XSS를 근본적으로 차단하지 않는다
  • 악성 스크립트를 판별하지 않는다
  • 서버 응답을 검증하지 않는다

즉, 이 헤더는
공격을 탐지하거나 차단하는 기능이 아니라
브라우저가 잘못된 선택을 하지 못하게 제한하는 옵션에 가깝다.

4-3. Referrer-Policy#

Referrer-Policy는
다른 출처로 이동할 때
얼마나 많은 요청 정보를 함께 전달할지를 제어한다.

이를 통해,
불필요한 URL 정보나 쿼리 스트링 노출을 줄일 수 있다.

하지만 이 설정 역시 다음을 보장하지 않는다.

  • 외부 요청 자체를 막지 않는다
  • 접근 권한을 제어하지 않는다
  • 민감한 데이터 보호를 자동으로 수행하지 않는다

이 옵션의 목적은
보안을 강화하는 것이라기보다,
정보 노출을 최소화하는 것에 가깝다.

4-4. Permissions-Policy#

Permissions-Policy는
브라우저가 제공하는 기능에 대한 접근을 제한한다.

예를 들어,
카메라나 마이크, 위치 정보와 같은 기능을
명시적으로 비활성화할 수 있다.

하지만 이 설정은 다음과 무관하다.

  • 서버 보안
  • 인증 상태
  • 사용자 권한 검증

즉, 이 옵션은
보안보다는 권한 최소화 원칙을 브라우저 수준에서 적용하는 도구에 가깝다.

이 보안 헤더들이 공통적으로 가지는 성격#

지금까지 살펴본 보안 헤더들은
모두 하나의 공통점을 가진다.

이 옵션들은:

  • 요청을 검증하지 않는다
  • 공격을 판별하지 않는다
  • 권한을 부여하거나 차단하지 않는다

대신,

  • 브라우저의 위험한 동작을 제한하고
  • 공격 표면을 줄이며
  • 실수로 보안을 깨뜨릴 가능성을 낮춘다

즉, 이 보안 헤더들은
보안을 제공한다기보다,
보안 사고가 발생하기 어려운 환경을 만든다고 보는 편이 정확하다.

Next.js 보안을 오해하기 쉬운 지점#

여기서 말하는 보안은
인증이나 권한 검증처럼
공격을 직접 차단하는 보안을 의미하지 않는다.

이 글에서 다루는 보안은,
공격이 성립하기 쉬운 환경을 미리 제거하는
브라우저 레벨의 보안이다.

정리#

이 글에서 다룬 보안 헤더들은
공격을 직접 차단하는 보안이 아니라,
공격이 성립하기 쉬운 환경을 미리 제거하는 장치들이다.

Next.js 보안은 옵션을 많이 켜는 것이 아니라,
각 옵션의 책임 범위를 정확히 이해하는 것에서 시작된다.

참고#

5