-
Notifications
You must be signed in to change notification settings - Fork 1
Cookie vs. Header
ref - JWT는 어디에 저장해야할까? - localStorage vs cookie
- 브라우저에서 js 코드로 localStorage에 접근 가능하기 때문에, XSS 취약점 존재
-
XSS (Cross Site Scripting)
- 공격자가 victim 웹사이트의 어떤 페이지에 악의적인 스크립트를 삽입함
- 불특정 사용자가 해당 페이지에 접근하면, html에 삽입된 script를 실행하게 됨
- js코드로 접근은 불가하나, 쿠키가 항상 보내지기 때문에 CSRF 취약점 존재
-
CSRF (Cross-Site Request Forgery)
- 공격자는 피해자가 방문한 신뢰할 수 있는 웹사이트에서 인증이 필요한 요청을 실행할 수 있는 URL, HTML 폼, 이미지 태그 등 악의적인 컨텐츠를 준비하여 피해자에게 전달
- 피해자가 해당 악의적 요청을 실행하면, 브라우저는 SOP 정책에 따라 자동으로 저장된 인증 쿠키를 함께 전송
- 서버는 피해자가 실제로 요청을 보낸 것으로 오인하고, 해당 요청을 정상적으로 처리함
CSRF 공격을 방지하기 위해 서버는 사용자가 로그인 시 CSRF 토큰을 추가로 발행하여 브라우저에 전송하는데, 이 CSRF 토큰은 쿠키가 아닌 다른 곳(ex: localStorage)에 저장됨. 사용자는 인증이 필요한 요청 시에 항상 이 CSRF 토큰을 추가적으로 보내야 하고, 서버는 인증 쿠키와 CSRF 토큰이 모두 유효해야만 요청을 처리함. 공격자가 CSRF 공격을 수행했을 때 CSRF 토큰에는 접근할 수 없기 때문에, 서버는 악의적인 요청을 식별하여 거부할 수 있음.
LocalStorage 방식의 flaw
- XSS는 CSRF보다 방어하기 까다로움
- CSRF에 비해 공격 스펙트럼이 넓다 (html, js, url 등을 통해 공격 가능)
- 방어를 위해 input sanitization, CSP(Content Security Policy) 등 조치가 필요하다
Cookie 방식의 flaw
- mdn은 저장소로 쿠키를 추천하지 않음
https://developer.mozilla.org/ko/docs/Web/HTTP/Cookies
과거엔 클라이언트 측에 정보를 저장할 때 쿠키를 주로 사용하곤 했습니다. 쿠키를 사용하는 게 데이터를 클라이언트 측에 저장할 수 있는 유일한 방법이었을 때는 이 방법이 타당했지만, 지금은 modern storage APIs를 사용해 정보를 저장하는 걸 권장합니다. 모든 요청마다 쿠키가 함께 전송되기 때문에, (특히 mobile data connections에서) 성능이 떨어지는 원인이 될 수 있습니다. 정보를 클라이언트 측에 저장하려면 Modern APIs의 종류인 [웹 스토리지 API] (
localStorage와sessionStorage) 와 [IndexedDB]를 사용하면 됩니다.

- 사용자가 구글 로그인 버튼을 눌러 OAuth 로그인을 시작한다.
- OAuth 페이지로 리다이렉트한다.
- 사용자가 구글 인증을 완료하면, OAuth 서버에서 인가 코드(Authorization Code)을 url param에 담아 백엔드 서버로 리다이렉트한다.
- 백엔드 서버는 인가 코드를 OAuth 서버에 보내 액세스토큰과 교환한다.
- 교환한 OAuth Access Token을 백엔드 DB에 저장하고, 우리 서비스의 자체 Access Token을 생성한 후 httpOnly 쿠키에 담아 웹서버 도메인으로 리다이렉트한다.
- httpOnly 쿠키는 JS로 쿠키에 접근할 수 없기 때문에, 클라이언트에서 로그인 여부 확인을 위해서는 API 호출 또는 미들웨어 사용이 필요함
- OAuth 액세스토큰 발급 이후 백엔드 서버에서 클라이언트의 특정 url로 리다이렉트해줘야 하기 때문에, 프론트엔드-백엔드 간 의존성이 생김
→ 클라이언트는 받은 토큰을 localStorage에 저장하고, 이후 요청 시 Authorization 헤더(Bearer ${token})에 담아 전송한다.

- 사용자가 구글 로그인 버튼을 눌러 OAuth 로그인을 시작한다.
- OAuth 페이지로 리다이렉트한다.
- 사용자가 구글 인증을 완료하면, OAuth 서버에서 인가 코드(Authorization Code)를 웹서버 도메인으로 리다이렉트한다.
- 클라이언트가 전달받은 인가 코드를 백엔드 서버에 보내고, 백엔드 서버는 OAuth 서버와 통신하여 액세스토큰을 교환한다.
- 백엔드 서버는 OAuth Access Token을 백엔드 DB에 저장하고, 우리 서비스의 자체 Access Token을 생성해 httpOnly 쿠키에 담은 뒤, 웹서버 도메인으로 리다이렉트한다.
배경:
구글 로그인 후 인증 코드가 서버로 전달되고 인증 과정을 거친 후에 헤더에 jwt토큰을 저장해서 리턴하고 있었다.
문제 상황
여기서 클라이언트가 jwt토큰을 못받는 문제가 발생하였다.
-
사용자는 구글 로그인 페이지로 이동하였고, 우리 서비스에서 벗어나 있는 상황이었다. 여기서 구글 인증 서버는 api 서버로 리다이렉션하게 되고 사용자는 api서버로 이동하게 되고 여기서 리턴하게 되면 jwt토큰이 우리 서비스로 전달되지 않는 문제이다.
해결 방안..?
서버에서 인증 코드를 인증하고 난 뒤에 바로 리턴하는게 아니라 클라이언트 주소로 리다이렉션 하면 해결이 가능했다. 하지만 리다이렉션을 사용하면서 jwt토큰을 전달하기 위해서는 쿠키를 사용해야 했다.
→ 그러면 쿠키에 jwt토큰을 저장해서 주고 받으면 해결되는가 싶었다.
문제 상황 (2)
당시 서비스가 배포되기 전이었기 때문에 로컬에서 api 서버를 띄워서 테스트를 진행하고 있었다. 로컬에서 진행하고 있었기 때문에