내배캠 주요 학습/TIL : Today I Learned

JWT 토큰은 어디에 저장해야 할까?

chaeyoung- 2023. 8. 25. 12:20
서버에서 발급한 Access Token 및 Refresh Token을 어디에 저장해야 할까?

 

 

현재 상황

현재 제 프로젝트에서 JWT 토큰을 활용합니다.

Access Token이 탈취됐을 때 대비를 위해 Refresh Token 개념을 도입하여, 두 토큰 모두 쿠키에 저장했습니다.

쿠키에 담아 JS 파일에서 토큰 여부를 확인하기 위해 setHttpOnly()를 설정하지 않았습니다.

 

 

 

문제점

클라이언트 측에서 둘다 갖고 있으면 탈취 당할 가능성이 있지 않을까?

 

 

 

고민

먼저, JWT 인증은 서버가 상태를 갖지 않아 클라이언트 측에서 토큰을 저장하고 있어야 합니다.

클라이언트가 사용할 수 있는 저장소는 쿠키, 세션 스토리지, 로컬 스토리지가 있습니다.

이 중 어떤 저장소에 저장할지 선택하기 위해 각 저장소별 차이점을 찾아보았습니다.

 

 

저장소 별 차이점으로 고려해야 할 점

첫번째는, "얼마나 오래 보관할 것인가?" 입니다.

  • 세션 스토리지에 저장된 데이턴느 브라우저를 닫으면 제거됩니다. 자동 로그인 등 새 브라우저를 열었을 때 유지하기 어렵습니다.
  • 로컬 스토리지는 영구 저장소이다. 따로 지워주지 않으면 브라우저에 계속 남아있다. 중요한 정보를 넣어두면 위험합니다.
  • 쿠키는 만료일을 저장하게 되어 있습니다.

 

두번째로, 가장 중요하게 고려할 사항은 "보안 위험 대비" 입니다.

토큰은 사용자 여부를 판단할 수 있는 도구입니다. 공격자가 토큰을 탈취하여 정당한 사용자인 척 할 수도 있습니다. 이런 위험을 최소화할 수 있는 저장소를 선택하여야 합니다. 이 부분은 의견이 분분한 지점입니다.

 

  • 로컬 스토리지는 JavaScript 코드를 통해 접근할 수 있어 XSS 공격에 취약합니다. 쿠키도 JS를 통해 접근 가능 하지만, 쿠키를 설정할 때 httpOnly 플래그를 설정하여 예방할 수 있습니다.
  • 또한, 쿠키가 모든 웹 요청에 함께 전송된다는 점이 취약할 수 있으나 쿠키를 만들 때 Secure flag를 설정하면 보안되지 않은 연결에서는 전송되지 않습니다.
  • 쿠키는 여러 옵션을 추가로 설정하여 로컬 스토리지의 취약점을 보완할 수 있습니다. 그렇지만 쿠키 역시 CSRF라는 공격에 취약합니다. 다만, XSS 공격이 범위가 더 넓고  방어가 힘들기에 쿠키에 저장하는 것은 권장하는 의견도 많다고 니다.

 

 

 

해결방안

Access Token을 로컬 스토리지 또는 세션 스토리지에 저장하고, 

Refresh Token은 쿠키에 저장하고 보안 옵션(HTTP Only)을 활성화 하였습니다.

 

1) HTTP Only Cookies
클라이언트에서 자바스크립트로 쿠키를 조회할 수 있는데 해당 옵션을 활성화 하면 브라우저에서 쿠키에 접근할 수 없으므로 XSS와 같은 공격으로부터 안전합니다.

cookie.setHttpOnly(true);

 

 

2) Secure Cookie 

웹브라우저와 웹서버가 HTTPS로 통신하는 경우에만 웹브라우저가 쿠키를 서버로 전송하는 옵션.

네트워크 통신 시에 송수신되는 패킷을 가로채는 스니핑 공격을 예방할 수 있습니다.

cookie.setSecure(true);

 

 

 

setHttpOnly()를 설정하면 js 파일에서 아래와 같은 코드를 사용할 수가 없습니다.

그러므로, Authorization에 해당하는 Access token은 HTTP only를 설정하지 않고 냅두었습니다.

$(document).ready(function () {
  pageSetting();
});


/* 페이지 첫 로드 메서드 */
function pageSetting() {  // main page 세팅
  const token = Cookies.get('Authorization');

  if(!token) {
    // 토큰 없을 경우, 로그인하지 않은 유저에게 보여줘야 할 내용을 처리하실 수 있습니다.
  }

  // 만료되지 않은 경우, 로그인한 유저에게 보여줘야 할 내용을 처리하실 수 있습니다.

}

 

대신, Refresh Token에만 SetHttpOnly() 설정을 추가하여 보안적으로 안전하도록 설정했습니다.

  public void addJwtToCookieAccessToken(String token, HttpServletResponse res) {
    token = URLEncoder.encode(token, StandardCharsets.UTF_8).replaceAll("\\+", "%20"); // Cookie Value 에는 공백이 불가능해서 encoding 진행

    Cookie cookie = new Cookie(AUTHORIZATION_HEADER, token); // Name-Value
    cookie.setPath("/");
    res.addCookie(cookie);
  }

  // JWT Cookie 에 refresh token 저장
  public void addJwtToCookieRefreshToken(String refreshToken, HttpServletResponse res) {
    refreshToken = URLEncoder.encode(refreshToken, StandardCharsets.UTF_8).replaceAll("\\+", "%20"); // Cookie Value 에는 공백이 불가능해서 encoding 진행

    Cookie cookie = new Cookie(REFRESH_HEADER, refreshToken); // Name-Value
    cookie.setPath("/");
    cookie.setSecure(true);
    cookie.setHttpOnly(true);
    res.addCookie(cookie);
  }

 

 

 

여기서 AccessToken에도 SetHttpOnly() 설정을 추가하여 보안을 높혔을 때,

클라이언트 측에서 인증 여부를 판단할 수 있는 로직을 구현하는 방법을 고민해봐야 할 것 같습니다.

 

 

 

 

 


참조:

https://velog.io/@znftm97/JWT-Session-Cookie-%EB%B9%84%EA%B5%90-sphsi9yh

 

JWT란? JWT , Session, Cookie 비교

세션기반 인증 방식과 토큰기반 인증 방식 모두 구현해 봤지만, 두 인증방식 모두 왜 쓰는지에 대해서는 모른채 구현했다. 적어도 이런 개념들이 왜 등장했고, 각 어떤 장단점이 있는지 알고 쓰

velog.io

https://velog.io/@hyojhand/Refresh-Token-%EB%B8%94%EB%9E%99-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EB%8F%84%EC%9E%85%EA%B8%B0

 

Refresh Token, 블랙 리스트 도입기

Access Token만 사용하여 사용자를 인증하던 서비스에서, Refresh token을 도입하면서 인증과정을 고민해보고, 블랙리스트(Blacklist)를 추가하여 로그아웃 후의 혹시모를 Access token 탈취상황까지 고려해

velog.io

https://velog.io/@mygomi/JWT%ED%86%A0%ED%81%B0-%EC%96%B4%EB%94%94%EC%97%90-%EC%A0%80%EC%9E%A5%ED%95%A0-%EA%B2%83%EC%9D%B8%EA%B0%80-%EC%9B%B9-%EC%A0%80%EC%9E%A5%EC%86%8C%EB%B3%84-%EC%B0%A8%EC%9D%B4

 

JWT토큰 어디에 저장할 것인가 : 웹 저장소별 차이

게시글 링크 첨부 예정JWT인증은 서버가 상태를 갖지 않아 클라이언트 측에서 토큰을 저장하고 있어야한다. 우리가 사용할 수 있는 저장소는 쿠키, 세션 스토리지, 로컬스토리지가 있다. (기존에

velog.io