TroubleShooting & Study/SpringBoot

[CS지식을 Spring에 접목해보자] JWT는 어디에 저장해야할까? (feat. Cookie, Local Storage)

DH_0518 2024. 10. 14. 01:05

 

 

 Spring에서 JWT를 사용한 인증/인가를 진행할 때, Server에서 Client로 AccessToken과 RefreshToken을 발행해줄 것이다. 이때 Cookie에 저장하는 방법과 로컬 스토리지에 저장하는 방법이 있는데, 이번 시간에는 두 방법의 장/단점을 알아보고 어떻게 사용하면 좋을지 고민해보자

 

 

 

 

 

 

Web Browser에 토큰을 저장할 때의 취약점

 

 

 우리는 성능 향상과 확장성을 위해 세션 대신 JWT를 사용한다. 성능 향상은 메모리 사용량과 DB Connection을 줄일 수 있어서 그렇다 치지만, 확장성은 어떻게 보장해줄까?

 그건 바로 서버에서 보내주는 토큰을 브라우저에 저장함으로써 서버에서 사용자의 인증 정보를 기억하지 않아도 되기 때문이다.

 그러나 이러한 확장성 때문에 브라우저에 토큰을 저장할 때, 몇 가지 공격들에 취약하다는 단점이 있다. 어떤 공격들인지 알아보자

 

 

 

CSRF(Cross-Site Request Forgery)

  • CSRF 공격은 해커가 유저의 의지와 무관하게 해커가 의도한 행동을 하도록 request를 위조하는 공격을 의미한다
  • 이 공격은 브라우저에 저장된 쿠키가 서버로 보내는 모든 Request에 자동으로 포함되어 전달되는 특성 때문에 일어난다.
  • 예시 상황을 통해 알아보자
    1. 해커는 유저가 이미지를 열람하도록 하거나 Link를 클릭하도록 유도한다
    2. 예를들어 게시글을 작성할 때, 이미지나 Link의 src에 해커가 의도한 Request URI를 입력한다
      (ex. src = http://mysite.com/like?post=1234, 1234번 게시글에 좋아요를 누르는 요청)
    3. 로그인을 해서 cookie에 access token이 설정되어있는 유저가 해커의 게시글을 조회한다
    4. 유저가 이미지를 조회하거나 Link를 클릭하면 src에 입력한 request가 보내지게 되고, request가 서버로 보내질 때 자동으로 쿠키에 저장된 유저들의 'access token'이 request에 포함된다
    5. 유저의 의지와 관계없이 해커의 게시물(1234번)에 좋아요가 올라간다

XSS(Cross-Site Scripting)

  • XSS 공격은 게시판이나 웹 메일 등에 JS와 같은 Script Code를 삽입 해, 개발자가 고려하지 않은 기능이 작동하게 하는 공격을 의미한다
  • 이를 통해 해커는 유저의 쿠키 정보나 로컬 스토리지에 저장된 값을 얻을 수 있다

 

 

 

 

 

LocalStorage? Cookie?

 

 

 

 이제 위의 두 가지 공격 방법을 읽었다면 한가지 드는 생각이 있을것이다.

 

"로컬스토리지는 XSS에만 취약한데, 쿠키는 CSRF랑 XSS 모두한테 취약하네? 그렇다면 로컬스토리지를 써야하는거 아니야?"

 

이런 생각이 떠올랐다면 다음 글에서 '쿠키의 속성' 파트를 읽어보고 오길 바란다.

https://kdh0518.tistory.com/entry/Network-Cookie

 

[Network] Cookie

쿠키(cookie)는 서버가 사용자의 웹 브라우저에 'Key:Value' 형태로 전송하는 작은 데이터 조각으로, 브라우저는 이런 문자열 데이터 조각들을 저장해두었다가 동일한 서버로의 request마다 쿠키 데이

kdh0518.tistory.com

 

 

 

1. Cookie에는 보안 속성이 있단다

 쿠키에는 XSS 공격을 예방할 수 있는 'HttpOnly' 속성과, CSRF 공격을 예방할 수 있는 'SameSite' 속성이 존재한다.

 

HttpOnly

  • HttpOnly 옵션을 설정하면 JS에서 쿠키에 접근 자체가 불가능해진다
  • 따라서 JS Script Code에 의한 공격을 막을 수 있다

SameSite

  • CSRF 공격 및 의도하지 않은 정보 유출을 막기 위한 옵션으로, 서로 다른 도메인간의 쿠키 전송에 대한 보안을 설정한다
  • Strict나 Lax로 설정하면 어느정도의 CSRF 공격을 예방할 수 있다

따라서 쿠키에 두 가지 보안 옵션을 설정해줬다면 XSS와 CSRF를 어느정도 예방할 수 있다

 

 

2. 그래서 Cookie를 사용해라고?

 그렇지 않다. Cookie에 HttpOnly를 설정하더라도 JS로 request를 보내면 토큰 값을 알 필요 없이 자동으로 담기기 때문에 위험하다(CSRF). 그렇다고 이를 예방하고자 SameSite를 사용한다면 Strict 전략은 사용자의 편의성을 많이 해치게 되고, Lax는 일부를 허용해주기에 완벽하게 막을 수 있다고 볼 수는 없다.

 

 

3. 어쩔 수 없이 Local Storage를 사용해야겠네?

 물론 Local Storage가 CSRF 공격에는 안전하지만, XSS 공격에 너무나도 취약하다. 해커가 JS 코드 한 줄만 주입하면 Local Storage를 내 집 처럼 드나들 수 있다.

 

 

4. 그래서 뭐 어쩌란거지..?

 명확한 정답은 없고 자신의 서비스와 인프라 환경에 따라서 적절한 방법을 사용하면 된다.

하지만 나는 열린 결말을 싫어하기에, 내가 찾아봤던 수 많은 블로그 들에서 추천하는 방법을 읽어보고, 나도 여러 사람들과 같이 고민하면서 제일 괜찮다 생각한 방법을 소개하고 마무리 짓겠다.

 

  1. Server에서 Client로 Token 전달
    • Client에서 로그인 요청이 들어오고, id/pw가 유효하다면 클라이언트로 토큰을 전달해줘야 할 것이다
    • 이때 Access Token은 ResponseBody에, Refresh Token은 Cookies에 담아서 전달한다
    • Cookie에는 'HttpOnly, Secure, SameSite(Lax)' 옵션을 설정해준다
      • HttpOnly = true 를 사용해서 XSS를 예방한다
      • Secure = true 를 사용해서 HTTPS 프로토콜에서만 요청을 보낼 수 있게 설정한다
      • SameSite = Lax 를 사용해서 몇몇 예외 사항을 제외하고 Cross-Site에서 쿠키를 사용할 수 없게 한다
  2. Client에서 토큰 저장
    • 서버로부터 넘겨받은 Access Token은 in-memory에 저장한다. 즉, 프론트에서 특정 variable을 만들고 거기에 Access Token을 담는다
      (ex. const accessToken = response.body.get("accessToken");)
      • 이 경우 유저가 페이지를 새로고침 하거나 사이트를 나간다면 곧바로 AccessToken이 사라지겠지만, 이를 위해 Cookie에 Refresh Token을 설정해주었으므로 전혀 상관이 없다
  3. Refresh Token을 사용해서 Access Token 재발급
    • 만약 유저의 Access Token이 만료되었거나 사라졌다면, Request를 보낼 때 자동으로 쿠키에 Refresh Token이 포함되었을 것이므로 Access Token을 재발급 받을 수 있다
    • 이때, Access Token을 재발급 받을 때 RTR(Refresh Token Rotation)를 적용해서 Refresh Token도 새로 발급해주면 훨씬 더 안전하게 사용할 수 있다

 

 

 

이상으로 Token 저장에 대한 깊은 고민을 해봤다. 항상 느끼는건데 개발에 은탄환은 없는 것 같다. 항상 자기 상황에 맞춰서 최선의 방법을 선택할 뿐..

 

 

 

 

 

 

 

 

 

 

 

 

혹시라도 잘못된 정보가 있을 수 있습니다. 피드백 주시면 감사하겠습니다 !

 

 

 

 

 

 

 

 

 

 

 

 

 

Reference

 

🌐 JWT 토큰 인증 이란? (쿠키 vs 세션 vs 토큰)

Cookie / Session / Token 인증 방식 종류 보통 서버가 클라이언트 인증을 확인하는 방식은 대표적으로 쿠키, 세션, 토큰 3가지 방식이 있다. JWT를 배우기 앞서 우선 쿠키와 세션의 통신 방식을 복습해

inpa.tistory.com

 

JWT는 어디에 저장해야할까? - localStorage vs cookie

이번에 지하철 미션을 만들면서 JWT를 클래스 property에 저장했었는데 리뷰어 분께 해당 부분을 피드백 받으면서 어디에 JWT를 저장하는 것이 좋을까 에 대해 고민해보게 되었다. 0. 기본 지식 JWT Js

velog.io

 

[SpringSecurity] CSRF란? / CSRF Filter 처리 방식

1. 개요 CSRF의 정의 Spring Boot에서의 CSRF Filter 처리 방식 2. CSRF란? 사이트 간 요청 위조(Cross-site request forgery, CSRF)는 웹사이트 취약점 공격의 하나로, 사용자가 자신의 의지와는 무관하게 공격자가

tlatmsrud.tistory.com

 

LocalStorage vs. Cookies: JWT 토큰을 안전하게 저장하기 위해 알아야할 모든것

안녕하세요 백엔드 개발자 최준혁입니다.

hshine1226.medium.com

 

Refresh Token Rotation 과 Redis로 토큰 탈취 시나리오 대응

I. 서론 JWT와 Session 비교 및 JWT의 장점 소개 II. 본론 Access Token과 Refresh Token의 도입 이유 Refresh Token 은 어떻게 Access Token의 재발급을 도와주는 걸까? Refresh Token Rotation Redis 저장 방식 변경 III. 결론

junior-datalist.tistory.com