본문 바로가기

Backend Development/Spring boot

[Spring boot] Chrome Cookie SameSite 해결하기

보통 Backend 에서 사용자 인증을 완료하고 Token 전달의 수단으로 Cookie를 많이 사용한다. Backend 로직에서 브라우저 Cookie 저장을 위해서 다음과 같이 보통 설정을 하게 된다.

 

package com.deeplify.tutorial.oauthlogin.utils;

import org.springframework.util.SerializationUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Base64;
import java.util.Optional;

public class CookieUtil {

    public static Optional<Cookie> getCookie(HttpServletRequest request, String name) {
        Cookie[] cookies = request.getCookies();

        if (cookies != null && cookies.length > 0) {
            for (Cookie cookie : cookies) {
                if (name.equals(cookie.getName())) {
                    return Optional.of(cookie);
                }
            }
        }
        return Optional.empty();
    }

    public static void addCookie(HttpServletResponse response, String name, String value, int maxAge) {
        Cookie cookie = new Cookie(name, value);
        cookie.setPath("/");
        cookie.setHttpOnly(false);
        cookie.setMaxAge(maxAge);
        cookie.setSecure(false);

        response.addCookie(cookie);
   }


여기서 잠시 위 addCookie에 사용된 인자들을 살펴보면

 

setPath : 쿠키가 적용될 url path를 지정한다.

setHttpOnly : http 방식으로만 Cookie 접근을 허용한다.

setMaxAge : 쿠키의 만료기간을 설정한다.

setSecure :  현재 ssl 등이 적용된 Secure Connection일 경우에 동작 시킨다.

 

대략 위의 의미를 담고 있다고 볼 수 있다.

 

그렇다면 위 쿠키 설정 구현을 바탕으로  Frontend (여기서는 Sample로 vue.js를 활용)와 Backend를 띄우고 쿠키 동작이 제대로 되는지 확인해 본다.

 

서버 동작을 위한 테스트용 기본 주소들은 다음과 같다.

 

Front end (dev server)

http://211.216.167.xxx:3000

 

Backend

http://kindlove.duckdns.xxx:8080

 

 

테스트 Backend를 실행하며 아래와 같이 간단한 로그인 버튼과 사용자 정보를 얻기위한 버튼이 보인다.

 

로그인을 해보면 login api는 정상 수행되서 token발급이 되었을 거고 cookie에 저장 시도를 하였을 것이다. 그라나 아래 user get api 를 보면 401 Unauthorized 에러가 발생함을 볼 수 있다.

 

 

F12를 눌러 디버거 창에서 상세 정보를 보면 api에서 Set-Cookie 요청을 하였으나 크롬 브라우저에서 허용을 안한것을 볼 수 있다.

 

최근 크롬 정책에 의해 서로 다른 도메인에서의 호출 즉 SameSite 가 아닌 상태에서의 호출은 브라우저에서 막는다는 예기다. 메세제를 보면 브라우저 기본으로 SameSite 값은 Lax로 설정되어있어서 다른 사이트에서 온 요청에 의한 Cookie 설정은 막는다고 되어 있다.

 

생각해보면 중요한 Cookie 정보가 다른 도메인에 전달이 된다면 해당 정보가 여러 사이트로 전달이 될 수 있으므로 보안측면에서 아래 정책은 이해가 간다.

 

단 메세지 마지막에 여지를 주고 있다. 즉 SameSite=None으로 설정을 하면 동작시킬 수 있다고 예기하고 있다.

 

 

Backend 에서 Set-cookie요청 시 SameSite=None 설정하기

 

Cookie 요청 시 SameSite 설정을 지정하려면 위에서 설명한 Cookie class로는 안되고 

 

import org.springframework.http.ResponseCookie;

 

위 ResponseCookie 클래스를 활용해야 한다.

 

아래와 같이 sameSite를 None으로 설정하고 서버 동작을 다시 해본다.

package com.deeplify.tutorial.oauthlogin.utils;

import org.springframework.http.ResponseCookie;
import org.springframework.util.SerializationUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Base64;
import java.util.Optional;

public class CookieUtil {

...

    public static void addCookie(HttpServletResponse response, String name, String value, int maxAge) {
        ResponseCookie cookie = ResponseCookie.from(name, value)
            .path("/")
            .sameSite("None")
            .httpOnly(false)
            .secure(false)
            .maxAge(maxAge)
            .build();

        response.addHeader("Set-Cookie", cookie.toString());
    }

 

여전히 User api에서는 401 에러가 나고 있다. 상세 메세지를 보면 여전히 브라우저에서 Set-Cookie를 허용하고 있지 않음을 볼 수 있다.

 

내용을 자세히 보면 Secure 옵션이 켜지지 않아서 그렇다고 한다. Cookie 생성 시 Secure 옵션을 enable 하고 Backend를 재구동해보자. 

 

아래와 같이 secure 옵션을 true로 해준다.

    public static void addCookie(HttpServletResponse response, String name, String value, int maxAge) {
        ResponseCookie cookie = ResponseCookie.from(name, value)
            .path("/")
            .sameSite("None")
            .httpOnly(false)
            .secure(true) -> secure 옵션을 true로 변경한다.
            .maxAge(maxAge)
            .build();

        response.addHeader("Set-Cookie", cookie.toString());
    }

 

여전히 Set-Cookie는 막혀있다. 그러나 이미 예상한 대로 실제로 secure 한 connection 에서 호출하지 않았다고 정확하게 집어주고 있다. 즉 실제로 backend는 http로 동작을 시키고 있고 Cookie 옵션만 secure = true로 한것이기 때문이다.

 

backend 가 실제로 ssl connection이 적용되도록 https 로 띄워주고 front에서도 https 로 bacend 를 호출하게 변경한 후 다시한번 확인해본다.

 

최종적으로 Set-Cookie가 허용됨을 알 수 있다. 즉 SameSite = None 설정으로 CrossSite 에서도 Cookie설정이 가능함을 볼 수 있다.

 

-- The End --