LabHub
开始
学习 学习路径 课程

会话与令牌 — 从浏览器到服务网格

不把令牌放在浏览器里的方法:BFF

在 LabHub 中继续学习

한국어 원문으로 표시합니다.

한 줄 요약

SPA 가 access token 을 직접 쥐면, 그 페이지에서 도는 스크립트 하나만 오염돼도 토큰이 밖으로 나간다. BFF(Backend for Frontend) 는 토큰을 서버에만 두고 브라우저에는 HttpOnly 세션 쿠키만 준다. 훔칠 토큰이 브라우저에 아예 없게 만드는 설계다.

왜 이게 필요했나

한동안 SPA 의 표준 구성은 이랬다. 브라우저가 발급자에게서 access token 을 받아 localStorage 에 넣고, API 를 부를 때마다 Authorization: Bearer 로 붙인다. 서버는 상태가 없어 편하다. 문제는 그 토큰을 읽을 수 있는 주체가 "이 페이지에서 도는 모든 스크립트" 라는 데 있다. 광고 태그, 분석 스크립트, 오염된 npm 패키지, XSS 한 줄 — 무엇이든 토큰을 읽어 공격자 서버로 보낼 수 있고, 그 토큰은 공격자 손에서 만료까지 유효하다. 사용자가 탭을 닫아도 소용없다.

IETF 는 이 문제를 오래 다듬어 RFC 10017 OAuth 2.0 for Browser-Based Applications(초안 이름 draft-ietf-oauth-browser-based-apps)로 냈다. 여러 구성을 비교한 뒤 6.1절에 BFF 를 둔다. 핵심은 한 문장이다 — 토큰이 BFF 에만 있으니, 브라우저에서 꺼내 갈 토큰이 없다.

어떻게 동작하나

BFF 는 프런트엔드와 같은 출처에 서는 작은 서버다. 이 서버가 OAuth 클라이언트 역할을 통째로 가져간다.

브라우저 ── GET /login ─────────────▶ BFF   (state 를 담은 임시 세션 발급)
브라우저 ── 302 ▶ 발급자 /authorize  (로그인)  ── 302 ▶ BFF /callback?code=..
BFF ─────── code + client_secret ──▶ 발급자 /token  → access token
BFF: 토큰을 서버 쪽 세션에 저장, 세션 ID 재발급, HttpOnly 쿠키만 내려줌
브라우저 ── GET /bff/api/me + 쿠키 ─▶ BFF ── Authorization: Bearer ▶ 하류 API

지켜야 할 규칙은 6.1.3절에 모여 있다.

현장에서 만나는 모습

BFF 를 붙인 뒤 가장 흔한 사고는 "편의상" 토큰이 다시 새는 것이다. 세션 확인 응답에 access_token 을 같이 실어 주거나, 프런트가 하류 API 를 직접 부르고 싶다며 토큰을 JS 가 읽는 쿠키로 하나 더 내려 준다. 그 순간 BFF 는 이름만 남는다. 반대 방향도 있다. 하류 API 가 "내부망이니까" 토큰 없는 요청을 받아 주면, BFF 를 건너뛴 호출이 그대로 통한다.

한계도 알고 써야 한다. 6.1.4.1절은 악성 스크립트가 사용자의 브라우저를 통해 BFF 로 요청을 보내는 클라이언트 하이재킹은 BFF 로도 막을 수 없다고 적는다. BFF 가 없애는 것은 토큰 탈취이지 XSS 자체가 아니다. 사용자가 탭을 닫으면 공격도 끝난다는 것, 그 차이가 BFF 의 값이다.

다음 실습에서 할 것

한 파드 안에 발급자(8312)·하류 API(8311)·BFF(8310)를 띄우고, 브라우저 역할을 curl 로 해서 로그인 흐름을 밟는다. BFF 가 내려 준 쿠키의 속성을 점검하고, Redis 의 BFF 세션에서 진짜 토큰을 꺼내 브라우저가 본 응답 어디에도 그 값이 없음을 확인한다. 이어서 BFF 경유 호출은 200, 하류 직접 호출은 401 인지, CSRF 토큰 없는 쓰기가 막히는지, 로그아웃 뒤 옛 토큰이 하류에서 죽는지까지 요청으로 증명한다.