Refresh Token을 쿠키에 보관해보자!

YUZE·2024년 11월 21일
1

Auction

목록 보기
1/5
post-thumbnail

RefreshToken은 유효기간이 긴 만큼, 클라이언트 단에서 안전하게 보관하는 것이 중요하다. 따라서, 이번 프로젝트에서, 서버에서 Refresh Token을 쿠키에 보관하도록 지정해서 수 있는 장소를 쿠키로 지정하기로 했다.

쿠키가 뭔가요?


서버가 어떤 데이터를 브라우저 측에 저장한 후, 다시 그 데이터를 받아오는 기술. 또는 그 데이터 자체를 뜻한다.


왜 쿠키인가?


Token을 쿠키에 http only 옵션와 secure 옵션을 true로 한다면, HTTPS에서만 접근이 가능하고 JS로 토큰에 접근을 하지 못하기 때문에 XSS, CSRF 위험으로 부터 방어를 할 수 있다.

실제로, Local storage와 Session storage에서 보관하는 것은 설정도 쉽고 편하지만, JS로 쉽게 접근할 수 있기 때문에 지양하는 것이 좋다.


쿠키 옵션에 대해 알아보자


1. Name=Value

  • 쿠키는 항상 이름과 값의 쌍으로 저장된다
Set-Cookie: sessionId=abc123;

2. Domain

  • 쿠키를 사용할 수 있는 도메인을 지정
  • 쿠키는 생성된 도메인에서만 유효하지만, Domain 속성을 사용하면 특정 도메인과 해당 서브도메인에서 쿠키를 사용할 수 있다.
Set-Cookie: userId=123; Domain=example.com;
  • example.comsub.example.com에서 쿠키 접근 가능.
  • another.com에서는 접근 불가.
  • 주의사항: 도메인을 잘못 설정하면 불필요한 사이트에서 쿠키에 접근할 수 있으므로 신중히 설정해야 합니다.

3. Path

  • 쿠키가 유효한 URL 경로를 지정
  • 지정된 경로 및 하위 경로에서만 쿠키가 전송된다.
Set-Cookie: cart=xyz; Path=/shop;
  • /shop/shop/details에서만 쿠키 전송
  • /home에서는 쿠키 전송 불가
  • 기본값: 쿠키가 설정된 경로

4. Expires

  • 쿠키의 만료 날짜를 설정
  • 지정된 날짜 이후 쿠키는 브라우저에서 삭제된다
Set-Cookie: token=abc; Expires=Wed, 29 Nov 2024 12:00:00 GMT;
  • 기본값: 설정하지 않으면 브라우저 세션 동안만 유지 (브라우저를 닫으면 삭제).

5. Max-Age

  • 쿠키의 수명을 초 단위로 설정
  • Expires와 비슷하지만 상대적인 시간을 지정한다.
Set-Cookie: token=abc; Max-Age=3600;
  • 쿠키는 1시간(3600초) 후 만료.
  • 우선순위: Max-Age가 설정된 경우 Expires보다 우선 적용

6. Secure

  • 쿠키가 HTTPS를 사용하는 요청에서만 전송
  • HTTP 요청에서는 전송되지 않아 Man-in-the-Middle 위험을 줄인다
  • 민감한 정보를 쿠키에 저장할 때는 항상 설정해야한다.
Set-Cookie: token=ab; Secure;

7. HttpOnly

  • 쿠키를 JavaScript에서 접근하지 못하도록 설정
  • XSS(Cross-Site Scripting) 공격으로부터 쿠키를 보호
Set-Cookie: token=ab; HttpOnly;

8. SameSite

  • 크로스사이트 요청에서 쿠키의 전송 여부를 제어
  • 옵션
    • Strict: 동일한 사이트 내에서만 쿠키 전송. 크로스사이트 요청에는 쿠키가 포함되지 않음.
    • Lax: 안전한 요청(GET, POST 등)에서는 크로스사이트 쿠키 전송 허용.
    • None: 모든 요청에서 쿠키 전송(단, Secure 속성 필요).
Set-Cookie: token=ab; SameSite=Strict;

예시


Set-Cookie: sessionId=abc123;
Domain=example.com;
Path=/;
Expires=Wed, 29 Nov 2024 12:00:00 GMT;
Secure;
HttpOnly;
SameSite=Strict;
  • sessionId: 쿠키 이름과 값
  • Domain: example.com 및 서브도메인에서 쿠키 접근 가능
  • Path: 모든 경로(/)에서 유효
  • Expires: 2024년 11월 29일에 만료
  • Secure: HTTPS에서만 쿠키 전송
  • HttpOnly: JavaScript에서 접근 불가
  • SameSite: 동일한 사이트 요청에서만 쿠키 전송 옵션



실제 프로젝트 코드에 적용 예시

    @PostMapping("/login")
    public ResponseEntity<ApiResult<AccessToken>> login(@RequestBody LoginRequest.Login login) {
        UserTokens tokens = authService.login(login.email(), login.password());
        AccessToken accessToken = AccessToken.of(tokens.getAccessToken());

        ResponseCookie cookie = ResponseCookie.from("refresh-token", tokens.getRefreshToken())
            .maxAge(COOKIE_AGE_SECONDS)
            .secure(true)
            .httpOnly(true)
            .sameSite("None")
            .path("/")
            .build();

        return ResponseEntity.ok()
            .header(SET_COOKIE, cookie.toString())
            .body(ApiResult.success(ApiResponseMessages.LOGIN_SUCCESS, accessToken));
    }

왜 ResponseCookie를 쓰는가?


  • ResponseCookie는 HttpCookie를 상속 받아 구현한 것이다
  • HttpCookie와 다르게, SameSite 옵션을 설정할 수 있다
  • 메서드 체인 형태로 옵션을 지정할 수 있다
  • ResponseCookie는 ResponseEntity의 헤더로 지정해서 응답을 할 수 있다 ⇒ HttpCookie를 클라이언트에 전송하려면 HttpServletResponse 객체를 사용해야 한다
     @GetMapping("/set-cookie")
        public String setCookie(HttpServletResponse response) {
            Cookie cookie = new Cookie("exampleKey", "exampleValue");
            cookie.setHttpOnly(true); 
            cookie.setSecure(true); 
            cookie.setPath("/"); 
            cookie.setMaxAge(3600); 
    
            response.addCookie(cookie);
            return "Cookie has been set";
       
    

번외) 현업에서는… 뭘쓰나?

현업에서는 어떻게 구현하는가?
⇒ 결론 : 다양하게 쓴다고…

profile
안녕하세요

0개의 댓글

관련 채용 정보