Java / / 2022. 8. 4. 08:58

[JWT 저장] localStorage vs Cookie 비교

반응형

🙌 서론

관리자 페이지의 로그인 기능과 로그아웃 기능을 만들게 되었다.

로그인 후 해당 사용자의 요청에 대해서만 응답하는 방법을 고민하게 되었다. 그러다 JWT 토큰 방식을 알게 되었다.

서버에서 JWT 토큰을 발급받고 난 뒤 클라이언트에서는 JWT를 어디에 저장해 두어야 가장 좋을까?라는 고민하며 서칭을 한 내용을 정리해 둔다.

 

📒 기본 지식

1. JWT

Json Web Token의 약자로 모바일이나 웹의 사용자 인증을 위해 사용하는 암호화된 토큰을 의미한다.

서버에서 발급받은 JWT 토큰을 Request에 담아 사용자의 정보 열람, 수정 등의 작업을 수행할 수 있다.

 

2. JWT 로그인은 어떻게 이루어 지나?

  • 유저가 로그인을 시도한다.
  • 서버가 인증정보를 보내주는데, 암호화나 시그니처 추가가 가능한 데이터 패키지안에 인증 정보를 담아서 보내준다. 이 패키지가 Json Web Token 즉 JWT이다.
  • 담기는 정보 중 accessToken과 refreshToken이 이후 유저 인증에 사용된다.
  • 이 정보를 클라이언트에 저장해 둔다.
    • 실질적인 인증 정보는 accessToken인데 일정 시간이 지나면 만료하는 구조를 가지고 있다.
    • refreshToken을 이용해 로그인을 지속적으로 유지할 수도 있다. refreshToken을 서버에 보내면 그때마다 새로운 accessToken을 발급해 돌려준다. (refreshToken사용은 옵션)
  • accessToken을 유저에게만 보여줄 수 있는 정보에 접근할 때 서버에 보면 된다.
  • 서버는 그 토큰이 유효한지 확인하는 방식으로 인증을 마친다.

3. XSS(Cross Site Scripting)

  • 공격자가 의도하는 악의적인 js 코드를 피해자 웹 브라우저에서 실행시키는 것이다

XSS라고 불리는 이유는 CSS가 이미 약자로 있기 때문이다.

code injection attack이라고도 한다.

이 방법으로 피해자 브라우저에 저장된 중요 정보들을 탈취 가능하다.

 

Javascript를 통해 사이트의 글로벌 변숫값을 가져오거나 그 값을 이용해 사이트인 척 API 콜을 요청할 수도 있다. 다시 말하면 공격자의 코드가 내 사이트의 로직인 척 행동할 수 있다는 거다.

 

4. CSRF

  • 공격자가 다른 사이트에서 우리 사이트의 API 콜을 요청해 실행하는 공격이다.
  • API 콜을 요청할 수 있는 클라이언트 도메인이 누구인지 서버에서 통제하고 있지 않다면 CSRF가 가능하다.(IP 허용)
  • 공격자가 클라이언트에 저장된 유저 인증정보를 서버에 보낼 수 있다면, 제대로 로그인한 것처럼 유저의 정보를 변경하거나 유저만 가능한 액션들을 수행할 수 있다.
  • 정상적인 Request를 가로채 피해자인 척하고 백엔드 서버에 변조된 Request를 보내 악의적인 동작을 수행하는 공격을 의미힌다.(피해자 정보 수정, 정보 열람) 

▶  내가 쓰지 않은 광고성 글이 SNS에 올라갈 수 있다.

  1. 공격자는 유저가 img를 열람하도록 하거나 link를 클릭하도록 유도한다.
  2. 이 action은 사용자 의도와는 관계없이 http request를 보낸다.
  3. 유저가 로그인이 되어있는 상태라면 이 request는 정상적으로 서버에 동작을 수행한다.

 

🙋‍♂️ XSS를 막으면 CSRF도 막히는 것 아닌가? NO

  img 태그나 링크로도 사용자 브라우저에서 악의적인 Request를 보낼 수 있다.

  사용자 브라우저의 js를 조작하는 XSS와는 성격이 다르다.

 

 


 

 

🙄 JWT? 그냥 아무 데나 저장하면 안 돼?

1. JWT는 개인정보나 다름없다.

보안 관련 대책 없이 JWT를 아무 곳에나 저장한다는 것은 우리 고객의 정보를 가져가도 상관없다는 의미와 같다.

 

2. JWT를 private variable에만 저장하면 안 될까?

js variable은 웹페이지를 새로고침 하면 사라지는 휘발성이다.

 

JWT를 private variable에 저장하는 것은 안되진 않지만 사용자 입장에서 화면을 새로 고침 할 때마다 새로 로그인을 시도해야 한다면 불편하고 귀찮을 것이다. 따라서 private variable에만 JWT를 저장하는 것은 피해야 한다.

 

 

😵 그럼 어디에 저장할 수 있을까?

  1. localStorage
  2. cookie

결론은 둘 중에 정답은 없다.

각각의 장단점이 존재하고 개발환경이 다르기 때문에(api를 사용해서 백엔드와 소통이 불가능한 경우), 무엇이 더 우월하다는 이야기는 할 수 없다.

 

그럼에도 Best Option은 존재한다.

 

1. localStorage에 저장

🥇 장점

CSRF 공격에는 안전하다.

그 이유는 자동으로 Request에 담기는 쿠키와는 다르게 JS코드에 의해서 헤더에 담기므로 XSS를 뚫지 않는 이상 공격자가 정상적인 사용자인 척 Request를 보내기는 어렵다.

 

🥉 단점

XSS에 취약하다.

공격자가 localStorage에 접근하는 JS코드 한 줄만 주입하면 localStorage를 공격자가 내 집처럼 드나들 수 있다.

 

2. cookie에 저장

🥇 장점

XSS 공격으로부터 localStorage에 비해 안전하다.

쿠키의 httpOnly 옵션을 사용하면 JS에서 쿠키에 접근 자체가 불가능하다.

그래서 XSS 공격으로 쿠키 정보를 탈취할 수 없다.(httpOnly 옵션은 서버에서 설정 가능)

 

하지만 XSS 공격으로부터 완전히 안전한 것은 아니다.

httpOnly 옵션으로 쿠키의 내용을 볼 수 없다 해도 JS로 Request를 보낼 수 있으므로 자동으로 Request에 실리는 쿠키의 특성상 사용자의 컴퓨터에 요청을 위조할 수 있기 때문이다.

공격자가 귀찮을 뿐이지 XSS가 뚫린다면 httpOnly cookie도 안전하진 않다.

 

🥉 단점

CSRF 공격에 취약하다.

자동으로 HTTP Request에 담아서 보내기 때문에,

공격자가 Requset url만 안다면 사용자 관련 link를 클릭하도록 유도하여 Request를 위조하기 쉽다.

 

※ 참고

최근에는 쿠키의 CSRF 취약점을 막기 위해 쿠키의 same-site 속성과 JS의 fetch api 속성의 기본값 등으로 Request에 쿠키를 싣지 않음이 설정되어 있다.

 


 

 

⭐ 결론

1. Best Option

  • refreshToken을 사용하는 방법

백엔드 API 개발자와 소통이 가능하다면,

refreshToken을 httpOnly 쿠키로 설정하고 url이 새로고침 될 때마다 accessToken을 발급받는다.

발급받은 accessToken은 JS private variable에 저장한다.

 

이 방법의 장점은,

refreshToken이 CSRF에 의해 사용된다 하더라도 공격자는 accessToken을 알 수 없다.

 

CSRF는 피해자의 컴퓨터를 제어할 수 있는 것이 아니다.

요청을 위조하여 피해자가 의도하지 않은 서버동작을 일으키는 공격방법이다.

따라서 refreshToken을 통해 받아온 response(accessToken)는 공격자가 확인할 수 없다.

 

따라서 쿠키를 사용하여 XSS를 막고, refreshToken 방식을 이용하여 CRSF를 막을 수 있다.

 

▶ 참고
프론트에서 안전하게 로그인 처리하기 (ft. React)
'store your refresh token in the cookie' 부분 참고

 

2. cookie vs localStorage, 굳이 1개를 선택한다면?

  • localStorage를 선호한다.
  1. cookie의 httpOnly 옵션도 XSS 공격을 완벽히 막을 수 없다. 어차피 XSS 방어는 필수적이므로 cookie의 장점이 매력적이게 보이지 않는다.
  2. cookie를 사용한다면 백엔드 API에 내가 사용하는 cookie를 위한 설정을 요구해야 한다. 백엔드와 조율이 잘 되는 상태라면 cookie를 사용해도 무방하지만, 불가능한 경우 localStorage가 더 좋다.
  3. mdn은 저장소로 쿠키를 추천하지 않는다. 대신 ModernStorage(localStorage와 sessionStorage)를 추천한다.

반대로, cookie를 지지하는 입장도 보자.

 

🙋‍♂️ cookie를 지지하는 입장

CSRF 공격은 다루기 쉬운 반면 프론트엔드 크기가 크면 클수록
XSS 공격을 막기 위한 작업은 많아지므로 쿠키 사용을 추천한다고 한다.

 

▶ 의견 참고

저는 cookie에 저장하는 편입니다. (가능하다면 httpOnly)
쿠키는 별도로 헤더에 담지 않아도 요청을 보낼 때 자동으로 담아서 보내지기 때문에
매번 서버로의 요청에 담아야 하는 토큰이라는 성격과 잘 맞고, 코드도 더 간결하게 작성할 수 있게 되는 것 같아요

 

 

▶ 참고 : 개인적인 기록을 위해 해당 포스팅을 참고하여 옮긴 내용입니다. 

https://velog.io/@0307kwon/JWT%EB%8A%94-%EC%96%B4%EB%94%94%EC%97%90-%EC%A0%80%EC%9E%A5%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C-localStorage-vs-cookie

 

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

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

velog.io

 

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유