YouMind
로그인

BFF 패턴을 사용하여 브라우저에서 토큰 제거하기

@farstep_
일본어2026년 6월 01일
295K
604
38
0
1.1K

TL;DR

이 글에서는 BFF 패턴이 서버 측에서 OAuth 토큰을 처리하고 HttpOnly 쿠키를 사용하여 SPA 애플리케이션을 보호하는 방법과, 이를 통해 XSS 취약점의 영향을 크게 줄이는 방법을 설명합니다.

OAuth BFF 패턴: 싱글 페이지 애플리케이션에서 토큰을 안전하게 다루는 아키텍처

싱글 페이지 애플리케이션(SPA)에서 OAuth를 처리할 때, 액세스 토큰과 리프레시 토큰을 어디에 저장할지는 오랜 논쟁거리입니다. localStorage, sessionStorage, 인메모리 변수 모두 XSS 공격에 취약합니다. BFF(Backend for Frontend) 패턴은 토큰을 브라우저가 아닌 서버 측에서 보관하는 설계 방식입니다. 이 글에서는 BFF 패턴의 작동 방식과 주요 구현 포인트를 정리합니다.

전제 조건

이 글은 다음 환경을 가정합니다:

  • OAuth 2.0과 OpenID Connect를 사용하는 브라우저 기반 앱 개발
  • SPA, 해당 SPA가 호출하는 API, 그리고 인증 서버의 존재
  • SPA와 이를 지원하는 서버 측 컴포넌트가 동일한 상위 도메인에 배치 가능
  • HTTPS는 필수(Secure Cookie 발급을 위해 필요)

브라우저 기반 앱의 XSS 위험

브라우저에서 실행되는 애플리케이션 코드는 브라우저 실행 환경에서 발생하는 모든 위협에 취약합니다. XSS의 영향 범위는 매우 넓으며, 공격 코드가 앱과 동일한 컨텍스트에서 실행되기 때문에 다음과 같은 작업이 가능합니다:

  • localStorage나 sessionStorage에 저장된 값 읽기
  • JavaScript에서 접근 가능한 인메모리 변수 읽기
  • 앱이 수행할 수 있는 모든 API 호출 실행
  • 내장 함수를 덮어써서 동작 변경(프로토타입 오염)

XSS의 진입점은 다양합니다. 의존 라이브러리의 취약점, 자체 코드의 입출력 처리 결함, 서드파티 스크립트의 손상 등이 있습니다. XSS를 완전히 차단하는 것은 어렵기 때문에 "침입이 발생하더라도 피해를 제한한다"는 현실적인 접근이 필요합니다.

토큰이 브라우저에 존재하는 한, XSS를 통한 토큰 탈취 가능성은 항상 존재합니다. 공격자가 탈취한 리프레시 토큰을 자신의 환경에서 사용하면, 사용자가 웹사이트를 닫은 후에도 오랫동안 API를 호출할 수 있습니다. 토큰 로테이션이나 유휴 시간 제한이 피해를 줄일 수는 있지만, 근본적인 해결책은 아닙니다.

토큰을 브라우저에 두지 않는 설계

BFF 패턴은 SPA 전용 서버 측 컴포넌트를 제공하고 OAuth 클라이언트의 책임을 서버로集中합니다. SPA는 OAuth 처리를 직접 하지 않고 BFF를 통해 인증과 API 호출을 수행합니다.

역할 분담은 다음과 같습니다:

  • 인증 서버와의 OAuth 프로토콜 통신: BFF가 처리
  • 액세스 토큰과 리프레시 토큰 보관: BFF만 수행
  • SPA와 BFF 간 인증 상태 유지: HttpOnly Cookie 사용
  • API 호출: SPA가 BFF에 요청을 보내면, BFF가 Cookie를 토큰으로 변환하여 API에 전달

이 구성에서 토큰은 BFF와 BFF가 호출하는 API 사이에서만 이동하며, 브라우저의 JavaScript에서는 볼 수 없습니다. XSS가 발생하더라도 공격자는 토큰을 추출하여 다른 곳에서 사용할 수 없습니다. 공격자가 할 수 있는 것은 사용자가 현재 열어놓은 세션 범위 내에서 BFF에 요청을 보내는 것뿐입니다. 이 영향이 무시할 수 있는 수준은 아니지만, 토큰 탈취에 비해 영향 기간과 범위가 크게 제한됩니다.

BFF는 OAuth 용어로 "기밀 클라이언트(confidential client)" 역할을 합니다. 클라이언트 시크릿을 보유하고 인증 코드를 토큰으로 교환하거나 리프레시 처리를 모두 서버 측에서 완료합니다.

인증 흐름

일반적인 인증 흐름은 다음과 같습니다:

farstep on X — cover
  1. SPA가 로그인을 시작하면 BFF에 로그인 요청을 보냅니다.
  2. BFF는 PKCE를 사용한 인증 코드 흐름을 시작하고, 인증 서버로의 리디렉션 URL을 생성하여 SPA에 반환합니다.
  3. SPA가 브라우저를 해당 URL로 이동시키면, 사용자가 인증 서버에서 인증을 수행합니다.
  4. 인증 서버가 BFF의 리디렉션 URI로 인증 코드를 반환합니다.
  5. BFF는 인증 코드를 토큰과 교환하여 액세스 토큰과 리프레시 토큰을 획득합니다.
  6. BFF는 토큰을 자체 보안 영역(암호화된 Cookie, 서버 측 세션 저장소 등)에 저장하고, HttpOnly Cookie를 통해 세션 식별자만 SPA에 반환합니다.
  7. SPA가 API를 호출할 때는 BFF를 통해 요청을 보냅니다. Cookie가 함께 전송되고, BFF가 이를 액세스 토큰으로 변환하여 업스트림 API에 전달합니다.
  8. 액세스 토큰이 만료되면 BFF가 리프레시 토큰을 사용하여 자동으로 갱신합니다.

SPA 입장에서는 Cookie로 로그인 상태가 유지되고, API 요청은 일반적인 fetch 호출로 처리됩니다. SPA에는 OAuth 토큰이나 인증 코드를 직접 처리하는 코드가 존재하지 않습니다.

필수 보안 설정

BFF가 발급하는 Cookie에는 다음 속성을 설정해야 합니다:

  • HttpOnly: JavaScript에서 접근 차단. XSS가 발생해도 Cookie 내용을 읽을 수 없습니다.
  • Secure: HTTPS를 통해서만 전송됩니다.
  • SameSite=Strict: 다른 사이트의 요청에는 Cookie가 전송되지 않습니다. 주요 CSRF 공격 경로를 차단할 수 있지만, 이 설정만으로 CSRF 방어가 완료되는 것은 아닙니다.
  • __Host- 접두사: Cookie 이름에 __Host- 접두사를 추가하면 브라우저가 Cookie를 발급 호스트로 제한하고 서브도메인과 공유하지 않습니다.

암호화된 Cookie(클라이언트 측 세션)에 토큰을 저장하는 경우 Cookie 내용이 암호화됩니다. 서버 측 세션 저장소(서버 측 세션)에 토큰을 배치하는 경우 Cookie에는 세션 식별자만 포함되므로 암호화가 필요하지 않습니다.

CSRF 방어: SameSite만으로는 부족

Cookie 기반 인증을 사용하므로 BFF는 CSRF 방어를 반드시 구현해야 합니다. SameSite=Strict는 유효한 조치이지만 전체 솔루션은 아닙니다. 특히 SPA가 www.example.com에, BFF가 api.example.com에 배치된 구성에서는 주의가 필요합니다. SameSite 결정은 origin이 아닌 site 단위로 이루어지기 때문에, example.com의 다른 서브도메인에서 오는 요청은 "same-site"로 간주되어 SameSite=Strict에서도 Cookie가 전송됩니다.

따라서 다음 방법 중 하나로 CSRF 방어를 강화해야 합니다:

  • BFF와 SPA의 origin이 다른 경우 CORS와 Origin 헤더 검증을 통해 방어
  • 상태를 변경하는 요청의 경우 CSRF 토큰을 anti-forgery / double-submit cookie 방식으로 검증

CORS를 정확한 Origin으로 제한

CORS는 SPA의 정확한 origin에 대해서만 허용해야 합니다. Cookie가 포함된 자격 증명 요청의 경우 브라우저는 Access-Control-Allow-Origin에 와일드카드(*)를 허용하지 않습니다. 따라서 BFF는 SPA의 정확한 origin을 응답에 반영해야 합니다. 이렇게 허용 origin을 엄격히 제한하는 것은 CSRF 방어의 일부로 기능합니다.

BFF가 보유한 세션 데이터, 특히 암호화된 Cookie에 저장된 토큰 정보에는 키 관리가 필요합니다. BFF 설정으로 키를 안전하게 주입하고 정기적으로 교체하는 작업을 포함해야 합니다.

SPA와 BFF를 동일한 상위 도메인에 배치하는 것은 Same-Site Cookie가 퍼스트파티 Cookie로 작동하기 위한 조건을 충족하기 위함입니다.

진화: API 중심 BFF

기존 웹 앱처럼 BFF를 구축하면 BFF의 서버 측 렌더링이 SPA의 페이지 전환에 관여하게 됩니다. API 중심 BFF는 이러한 영향을 최소화합니다.

역할은 두 가지로 나뉩니다:

  • OAuth Agent: OAuth 프로토콜 처리를 담당하는 API입니다. SPA에서 JSON을 통해 호출됩니다.
  • OAuth Proxy: API 게이트웨이 플러그인으로 작동하며, Cookie에서 토큰을 추출하여 업스트림 API에 전달합니다.

이 구성에서 SPA는 OAuth Agent를 일반 REST API처럼 호출합니다. SPA 프론트엔드 개발 경험은 BFF 도입 전과 거의 동일하게 유지할 수 있습니다.

도입 시 고려사항

BFF 패턴을 도입할 때는 다음 사항을 고려해야 합니다:

  • 추가적인 아키텍처 구성 요소로 인해 개발 및 운영 비용이 증가합니다.
  • SPA와 BFF를 동일한 상위 도메인에 배치해야 합니다.
  • 리버스 프록시가 단순히 Authorization 헤더를 전달하는 구성은 BFF가 아닙니다. BFF의 핵심은 기밀 클라이언트로서 토큰을 보유하는 것입니다.
  • PKCE와 BFF는 대체 관계가 아니라 함께 사용됩니다.
  • BFF는 전달 대상 호스트를 검증하여 의도치 않은 호스트로 토큰이 노출되는 것을 방지해야 합니다.
  • 로그아웃 설계가 복잡해집니다. SPA, BFF Cookie, 인증 서버 세션을 모두 연동해야 하기 때문입니다.

요약

브라우저 기반 앱에서 토큰을 안전하게 처리하는 실용적인 방법은 토큰을 브라우저에 두지 않는 것입니다. BFF 패턴은 OAuth 클라이언트 책임을 서버 측으로 이동시키고, HttpOnly Cookie만 브라우저에 전달합니다. 이를 통해 XSS가 발생해도 토큰 탈취를 방지할 수 있습니다. OAuth Agent와 OAuth Proxy로 역할을 분담하면 SPA 개발 경험을 유지하면서 보안을 강화할 수 있습니다.

원클릭 저장

YouMind로 바이럴 글을 AI 심층 읽기

소스를 저장하고, 핵심 질문을 던지고, 주장을 요약해 바이럴 글을 다시 활용할 수 있는 노트로 바꾸세요. 하나의 AI 워크스페이스에서 모두 할 수 있습니다.

YouMind 둘러보기
크리에이터를 위해

당신의 Markdown을 깔끔한 𝕏 글로

직접 쓴 장문을 올릴 때 이미지, 표, 코드 블록을 𝕏에 맞게 정리하는 일은 번거롭습니다. YouMind는 전체 Markdown 초안을 깔끔하고 바로 게시할 수 있는 𝕏 글로 바꿔 줍니다.

Markdown → 𝕏 사용해 보기

분석할 패턴 더 보기

최근 바이럴 아티클

더 많은 바이럴 아티클 보기