이전에 만들었던 RefreshToken을 이용한 AccessToken 재발급 기능에 문제가 생겼다.
현재 상황은 백엔드에서 AccessToken 재발급 기능을 구현하고 단위 테스트와 포스트맨 요청도 모두 성공한 상태였다. 이제 남은 건 프론트엔드에서 구현뿐
그런데 프론트엔드 팀원분이 기능을 만들어 시도해본 결과 원하는 응답이 아닌 401 에러가 발생했다.
먼저 이 소식을 처음 들었을 때, 좀 당황스러웠다.
서버에서 401 에러가 발생하는 경우는 프론트에서 accessToken을 보내주었는데 사용자 인증이 안된 경우에만 발생한다. 그런데 accessToken을 보내주지 않는 토큰 재발급 기능에서 401 에러가 발생할 수가 없기 때문이다.
서버 로그를 확인해본 결과 예상대로 만료된 accessToken으로 발생하는 문제인 걸 확인할 수 있었다.
그래서 프론트 팀원분께 서버에 요청을 보낼 때, 만료된 accessToken을 같이 보내주었는지 여쭤봤지만, 아니라는 답을 받았다. 하지만 팀원분과 같이 개발자 도구를 이용해 Network를 확인해보니 역시나 accessToken을 보내주고 있어서 문제가 생긴 걸 확인할 수 있었다.
토큰 재발급 시 헤더에 Authorization이 추가되어 있어서 나타난 현상. 프론트엔드에서 토큰 재발급 요청 시 Authorization을 포함하지 않게 하여 해결
앞선 401 문제를 해결하고 나니 이번엔 400 에러가 발생했다.
먼저 서버 로그를 통해 문제를 파악해봤다. 로그를 확인해 본 결과 요청을 보낼 때, 필수값인 RefreshToken을 쿠키로 보내주지 않고 있다는 걸 파악할 수 있었다.
withCredentials 옵션은 서로 다른 도메인(크로스 도메인)에 요청을 보낼 때 요청에 Credential 정보를 담아서 보낼 지를 결정하는 항목이다.
따라서 이 부분을 먼저 확인했는데, 확인 결과 true로 설정되어 있으며 문제가 없는 걸 확인했다.
Network 창을 살펴보니 SameSite라는 옵션이 존재하는 걸 파악할 수 있었다. 또한 Chrome의 경우에는 따로 설정해주지 않으면 자동으로 Lax옵션으로 지정된다는 걸 알 수 있었다.
SameSite는 다른 도메인 간의 통신에 대한 쿠키의 보안에 대한 옵션으로 다음 3가지 옵션을 지정할 수 있다.
현재 적용된 옵션은 Lax, 그리고 서버와 프론트엔드는 서로 다른 도메인이다. 따라서 POST 요청인 토큰 재발급 기능에서는 쿠키를 전달할 수 없다.
그렇다면 옵션을 None으로 바꾸면 모든 사이트에서 쿠키 전송이 가능해진다는 걸 예상해볼 수 있다.
그래서 SameSite 설정을 None으로 변경하고 secure 옵션을 적용하여 https 프로토콜에서만 쿠키 전송이 가능해지도록 하였다.
결과는 쿠키가 정상적으로 전달되어 토큰 재발급 기능이 정상적으로 작동했다.
일단 해결은 되었지만 SameSite를 None으로 하는 게 진짜 해결한 게 맞을지 의문이 들었다.
Chrome에서 기본을 Lax로 하는 이유는 쿠키의 사용처를 명확하게 함으로써 정보 유출 위험을 완화하고, CSRF(사이트 간 요청 위조) 공격으로부터 보호하기 위해서이기 때문이다. 그런데 단순히 동작하게 하자고 None으로 설정해버리면 보안적으로 위험할 수 있기 때문이다.
다른 해결방법은 어떤게 있을까 생각해본 결과, 서버와 프론트를 SameSite, 즉 동일 사이트로 만드는 방법을 떠올릴 수 있었다.
그러기 위해서 먼저 동일 사이트에 대한 기준을 찾아봤다.
SameSite의 기준
https://site.com:443 도메인으로 예시를 들어보겠다.
- https://site.com:443 (동일 사이트)
- "http://site.com:443 (동일 사이트) - 프로토콜 상관없음
- https://site.com:80 (동일 사이트) - 포트 상관없음
- https://sub.site.com:443 (동일 사이트) - 하위 도메인 상관없음
- https://abc.com:443 (교차 사이트) - 다른 도메인
참고로 동일 사이트와 동일 출처는 다른 개념이기 때문에 주의하자
따라서 서브 도메인인 https://chatchat-web.site/ 에서 서브 도메인(https://client.chatchat-web.site/)을 이용하여 프론트엔드를 배포하면 동일 사이트, 즉 SameSite가 되어 Lax 상태일 때, post 요청의 쿠키 전달이 가능해진다.
처음 문제가 생겼을 때, 포스트맨은 정상 작동 되는 상태이니 프론트엔드 측면에서 문제가 있을 것이라고 예상했다. 그래서 에러에 대한 대응이 늦어질 수밖에 없었다.
알아본 결과 포스트맨은 브라우저와 다른 방식을 갖고 있어 보안 제약을 따르지 않았기 때문에 문제가 생기지 않았던 것이었다.
앞으로는 프론트에서 문제가 생기더라도 단순히 프론트의 문제가 아닐 수 있음을 인지하고, 서버 로그와 Network 탭을 활용하여 문제해결에 적극적으로 나서도록 해야겠다.