이전 게시물을 통해 살펴보았듯, SSO 모델을 선택한 이후에도 문제가 남았다.
여러 사이트 간 데이터를 공유하기 위해 하나의 JWT 토큰을 둘 건데, 이걸 사이트 간 어떻게 공유해야 할까?
이 문제를 해결할 수 있는 방법을 조사해서 몇 가지 추려 보았다
일단 클라이언트 단에서 특정 정보를 저장해야 하는 것은 분명했다.
서버에서 데이터를 가지고 있다면, 클라이언트가 스스로임을 인증하기 위해 매번 다시 로그인을 해야하므로, 당초 목표였던 '로그인 빈도 낮추기'를 이룰 수 없기 때문이다.
(물론, 이렇게 구현하는 경우에도 로그인 횟수를 n회에서 1회로 줄일 수는 있지만, 0회(액세스/리프레시 토큰 활용)로 줄일 수는 없다)
따라서 클라이언트에서 정보를 저장하는 3가지 방식을 모두 알아보며 판단하였다.
(물론 3가지 방법이 서로 상호배제적이지는 않다. 세션 방식도 식별자를 작성하기 위해 쿠키를 사용하는 등 공통점이 있지만, 구현의 중심이 되는 기술을 기준으로 나누었다.)
자세한 기술적 설명은 게시물의 분량상 생략하였다.
세션 방식은 방문자가 웹사이트에 방문해서 인가를 받았을 때, 그에 대한 Session ID를 브라우저에 저장해 향후 통신에서 활용하는 방식이다.
세션 방식도 같은 브라우저에서 열린 같은 서브도메인 간 인증 내용을 공유해 충분히 활용할 수 있다.
하지만 치명적인 문제점이 있는데, 브라우저를 재시작하면 세션 정보가 초기화 되어 다시 로그인해야한다는 것이다.
따라서 로그인 횟수를 최소화하려는 우리의 시도에는 적합하지 않았다.
또한, 개발 스택이 다른 웹페이지 간 세션 공유는 상당히 어려운데, 우리 프로젝트의 웹사이트들은 Django를 사용하는 사이트와 Spring을 사용하는 사이트 모두 있어 실제 구현이 어려울 것으로 예상되었다.
로컬 스토리지 방식은 이미 개발되어 있는 메인 사이트에서 사용자 인증 정보를 저장할 때 사용되는 방식이기 때문에, 1순위로 고려하게 되었다.
하지만, 이 역시 이번 SSO 도입에 활용하기는 어려웠다. 일반적으로 로컬 스토리지의 정보는 Same-Origin Policy로 인해 서브도메인 간 공유가 어려우며, 이를 허용하기 위해서는 별도로 CORS 설정을 하는 등의 노력이 필요했다.
따라서 반드시 필요한 경우가 아니라면 보류하기로 하였다.
쿠키 방식은 메인 사이트에서는 활용하고 있지 않은 방법이였지만, 새로 구현하는 것이 그렇게 어렵지 않다는 장점을 가진다.
쿠키 도메인의 스코프를 확장하여 서브도메인 간 데이터 공유를 구현하는 것이 비교적 간단하게 느껴졌고, 만약 서브도메인 간 공유를 할 수 없게 되더라도 서드파티 쿠키 기술이 있기 때문에 보험이 생겼다.
물론 대부분의 애드블락이 서드파티 쿠키를 막고 있기 때문에 벼랑끝까지 몰리지 않는 이상 사용하고 싶지 않다.
당연하게도, SSO 구현에 제일 적합해 보이는 것은 쿠키 방식이었다.
이를 통해 구현해보기로 결정했다.
SSO에도 여러 방식이 있다.
각각 다음과 같다.
위 방식 중에서 이번 프로젝트에 가장 적합한 방식은 One Cookie Domain SSO이다.
모든 웹 서비스가 cau-likelion.org
라는 도메인에 속하기 때문에, 쿠키의 스코프만 확장해준다면 하나의 쿠키 도메인 내에서 구현이 가능하기 때문이다. 구현이 간편한 건 덤.
결정을 내렸으니 코드를 쓰기 시작해야한다. 다음은 프로젝트 진행 중 고려해야 할 점들이다.
쿠키의 스코프를 넓히는 것이 마냥 좋지는 않은 이유 중 하나이다.
Subdomain takeover란, 공격자가 DNS에서 삭제된 서브도메인을 선점하고 장악해 쿠키를 탈취하고 제어권을 얻는 것이다. 위에서 언급한 공격은 이의 변종이다.
react-cookie
를 통해 api.example.com
의 쿠키의 스코프를 넓히기 위해 domain: .example.com
과 같이 example.com
의 서브도메인을 전부 허용하는 코드를 작성하는 경우를 가정하자.
이 경우 evilcode.example.com
나 evilcode.api.example.com
과 같은 도메인의 쿠키를 허용하게 되므로, 공격자가 이를 활용할 수 있게 된다.
Replay attack 이란, 공격자가 노출된 사용자의 토큰을 이용해 인증을 받아 들어올 수 있다. 이에 대응하기 위해 토큰의 유효시간을 매우 짧게 주고(약 15분) 유효시간 이전에 꾸준히 재발행하여 공격자가 침입할 시간을 줄이는 것이 도움이 된다.
웹 개발자라면 누구나 한번씩 골치를 썩는 CORS가 문제가 될 수 있다. 쿠키는 기본적으로 SameSite로 설정되어 있기 때문에 다른 도메인으로 전송되지 않는다. 이를 다른 도메인에 보내기 위해서는 별도의 설정을 거쳐야한다. 참고 블로그
결정을 모두 내렸으니 이제 코드를 쓰러 가자.
react-cookie :: 서브 도메인이 다를 때, 같은 쿠키 값 공유하기
sso - 구현 방법, 쿠키 연동
Attacking SSO With Subdomain Takeovers
서로 다른 도메인 간 통합인증(SSO) 구현 방법
SSO 모델과 보안 기술