본문 바로가기

트러블 슈팅

클라이언트-서버 cookie 사용 문제

상황

Jwt 웹 토큰을 사용하면서, RefreshToken은 보안 상의 문제로 Cookie를 통해 프론트 서버로 전송하려고 상황에서 프론트에서 cookie를 사용하지 못하는 에러 상황이 발생했다.

 

❗ 초기 Cors 설정

registry
    .addMapping("/api/**")
    .allowedOriginPatterns("*")
    .allowedMethods("*")
    .allowedHeaders("*")
    .allowCredentials(true);

 

Set-Cookie

ResponseCookie refreshTokenCookie = ResponseCookie.from(REFRESH_TOKEN_HEADER, refreshToken)
    .httpOnly(true)
    .maxAge(refreshExpiredTimeMs)
    .build();
httpServletResponse.addHeader(HttpHeaders.SET_COOKIE, refreshTokenCookie.toString());

◼ 백엔드 서버에서 위와 같이 Set-Cookie 헤더에 RefreshToken Cookie를 담아서 응답하면, 브라우저에서는 해당 서버로 요청을 보낼 때마다 HTTP 헤더 Cookie 필드에 응답받은 Cookie 값을 같이 보내게 된다.

 

원인 

Same-Site-Policy

2020년부터 크롬은 SameSite 쿠키를 별도로 신설해, 다른 출처로 부터 전송되는 요청에 대해서 쿠키 전송을 제한하도록 했다.

 

First-Party Cookie vs Third-Party Cookie

◼ 쿠키에 설정된 도메인을 기준으로 First-Party Cookie와 Third-Party Cookie로 나눌 수 있다. 현재 사용자가 접속한 페이지와 쿠키에 설정된 도메인이 일치하는가?

◼ 만일 www.example.com에서 사용자의 요청으로 api.example.com 서버에서 쿠키를 응답 받았다면, 두 도메인이 달라 Third-Party Cookie이다.

 

SameSite 정책은 다음 3가지 중 하나를 선택할 수 있다. (default: Lax)

✔ None

◼ 다른 출처의 요청(서드 파트 쿠키)에도 쿠키를 사용할 수 있다. 단, Secure 속성을 true로 해야 한다.

만일, SameSite를 None으로 설정해도 Secure 속성을 false로 하면 쿠키를 사용할 수 없다. 즉, https 설정을 해야 한다.

 

✔ Lax

◼ 대부분의 서드 파티 쿠키를 전송하지 않지만, 예외적인 케이스에서만 허용한다. (ex, 유저가 링크를 누르거나 리다이렉트를 통해 이동하거나 서버의 상태를 바꾸지 않을 것이라 기대하는 GET 요청...)

✔ Strict

◼ 오직 동일 출처의 요청에서 생성된 퍼스트 파티 쿠키만을 전송한다.

 

📒 해결방안

1. 가장 먼저, 서버에도 https를 적용하기 위해 nginx(프록시 서버)를 사용해서 적용하였다.

 

2. ResponseCookie 설정

ResponseCookie refreshTokenCookie = ResponseCookie.from(REFRESH_TOKEN_HEADER, refreshToken)
    .secure(true)
    .httpOnly(true)
    .sameSite("None")
    .maxAge(refreshExpiredTimeSec)
    .build();
httpServletResponse.addHeader(HttpHeaders.SET_COOKIE, refreshTokenCookie.toString());

◼ responseCookie에 secure 속성과 sameSite 속성을 추가하였다.

 

◼ 위와 같이 Set-Cookie에 RefreshToken Cookie가 응답으로 잘 오는 것을 확인할 수 있다.

 

◼ 위와 같이 Request Header에서도 Cookie에 잘 적용되는 것을 확인할 수 있다.

 

결론

💥 프론트, 백 서버를 다른 도메인으로 배포하고 Cookie를 사용하기 위해서는 https를 적용하고 sameSite 정책을 고려해서 사용해야 한다.

 

🛠 추가로, 프론트에서 withCredentials=true를 설정하고 요청해야 Cookie를 사용할 수 있다. 이 부분으로 인해 많은 삽질을 경험하였다...

 

💬 Credentials 이란?

쿠키, Authorization 인증 헤더, TLS client certificates 를 내포하는 자격 인증 정보를 말한다.

기본적으로 axios 등의 라이브러리를 사용해서 API 요청, 응답을 진행하면, 쿠키와 같은 인증과 관련된 데이터를 함부로 사용하고 받을 수 없다. 이러한 것을 허용해주는 것이 withCredentials=true 옵션이다.