SSO 파헤치기 - 서브 도메인 간 토큰 공유하기!

나상현·2023년 8월 30일
1
post-thumbnail

문제

이전 게시물을 통해 살펴보았듯, SSO 모델을 선택한 이후에도 문제가 남았다.

여러 사이트 간 데이터를 공유하기 위해 하나의 JWT 토큰을 둘 건데, 이걸 사이트 간 어떻게 공유해야 할까?

이 문제를 해결할 수 있는 방법을 조사해서 몇 가지 추려 보았다

선택지

일단 클라이언트 단에서 특정 정보를 저장해야 하는 것은 분명했다.
서버에서 데이터를 가지고 있다면, 클라이언트가 스스로임을 인증하기 위해 매번 다시 로그인을 해야하므로, 당초 목표였던 '로그인 빈도 낮추기'를 이룰 수 없기 때문이다.
(물론, 이렇게 구현하는 경우에도 로그인 횟수를 n회에서 1회로 줄일 수는 있지만, 0회(액세스/리프레시 토큰 활용)로 줄일 수는 없다)

따라서 클라이언트에서 정보를 저장하는 3가지 방식을 모두 알아보며 판단하였다.
(물론 3가지 방법이 서로 상호배제적이지는 않다. 세션 방식도 식별자를 작성하기 위해 쿠키를 사용하는 등 공통점이 있지만, 구현의 중심이 되는 기술을 기준으로 나누었다.)

자세한 기술적 설명은 게시물의 분량상 생략하였다.

1. 세션

세션 방식은 방문자가 웹사이트에 방문해서 인가를 받았을 때, 그에 대한 Session ID를 브라우저에 저장해 향후 통신에서 활용하는 방식이다.
세션 방식도 같은 브라우저에서 열린 같은 서브도메인 간 인증 내용을 공유해 충분히 활용할 수 있다.

하지만 치명적인 문제점이 있는데, 브라우저를 재시작하면 세션 정보가 초기화 되어 다시 로그인해야한다는 것이다.
따라서 로그인 횟수를 최소화하려는 우리의 시도에는 적합하지 않았다.

또한, 개발 스택이 다른 웹페이지 간 세션 공유는 상당히 어려운데, 우리 프로젝트의 웹사이트들은 Django를 사용하는 사이트와 Spring을 사용하는 사이트 모두 있어 실제 구현이 어려울 것으로 예상되었다.

2. 로컬스토리지

로컬 스토리지 방식은 이미 개발되어 있는 메인 사이트에서 사용자 인증 정보를 저장할 때 사용되는 방식이기 때문에, 1순위로 고려하게 되었다.
하지만, 이 역시 이번 SSO 도입에 활용하기는 어려웠다. 일반적으로 로컬 스토리지의 정보는 Same-Origin Policy로 인해 서브도메인 간 공유가 어려우며, 이를 허용하기 위해서는 별도로 CORS 설정을 하는 등의 노력이 필요했다.
따라서 반드시 필요한 경우가 아니라면 보류하기로 하였다.

3. 쿠키

쿠키 방식은 메인 사이트에서는 활용하고 있지 않은 방법이였지만, 새로 구현하는 것이 그렇게 어렵지 않다는 장점을 가진다.
쿠키 도메인의 스코프를 확장하여 서브도메인 간 데이터 공유를 구현하는 것이 비교적 간단하게 느껴졌고, 만약 서브도메인 간 공유를 할 수 없게 되더라도 서드파티 쿠키 기술이 있기 때문에 보험이 생겼다.

물론 대부분의 애드블락이 서드파티 쿠키를 막고 있기 때문에 벼랑끝까지 몰리지 않는 이상 사용하고 싶지 않다.

선택

당연하게도, SSO 구현에 제일 적합해 보이는 것은 쿠키 방식이었다.
이를 통해 구현해보기로 결정했다.

SSO 방식

SSO에도 여러 방식이 있다.
각각 다음과 같다.

  • One Cookie Domain SSO
    모든 웹서비스가 하나의 쿠키 도메인 안에 있을 때, 토큰은 쿠키 도메인 내의 쿠키로 설정되어 모든 웹서비스에 하나의 토큰으로 인증할 수 있도록 하는 방식
  • Multi Cookie Domain SSO(Cross Domain SSO)
    웹서비스가 여러 도메인으로 나뉘어 있을 때, 각 도메인의 인증 및 토큰 발행을 맡을 별도 에이전트를 구성하는 방식. 별도 에이전트가 있다는 점에서 중앙화 되어 있으며, 이 에이전트는 각 사이트와 연결되어 인증을 수행한다.
  • One Token for All Multi Cookie Domain SSO
    위 두 방식을 합친 방식으로, 여러 개의 도메인에서 하나의 토큰을 사용하는 별도 에이전트를 구성해 인증하는 방식. 이 에이전트는 유저가 하나의 웹서비스에 로그인할 때 토큰을 발행하며, 이때 발행되는 토큰은 모든 도메인에서 유효하게 사용될 수 있다. 다만 보안의 관점에서 토큰이 유출되는 경우, 모든 도메인에 위협을 끼친다.
  • One Token for each Cookie Domain & One Token for Master Agent SSO
    에이전트와 각각의 도메인이 자신만의 토큰을 가져 보안 위협을 낮춘 방식. 토큰이 별도 발행되기 때문에 토큰에 담긴 사용자 정보도 조절 가능하다. 하나의 토큰이 유출되더라도 피해의 정도를 줄일 수 있다.

위 방식 중에서 이번 프로젝트에 가장 적합한 방식은 One Cookie Domain SSO이다.
모든 웹 서비스가 cau-likelion.org 라는 도메인에 속하기 때문에, 쿠키의 스코프만 확장해준다면 하나의 쿠키 도메인 내에서 구현이 가능하기 때문이다. 구현이 간편한 건 덤.

향후 고려할 점

결정을 내렸으니 코드를 쓰기 시작해야한다. 다음은 프로젝트 진행 중 고려해야 할 점들이다.

쿠키의 스코프 확장: Subdomain Takeover 공격에 취약해짐

쿠키의 스코프를 넓히는 것이 마냥 좋지는 않은 이유 중 하나이다.
Subdomain takeover란, 공격자가 DNS에서 삭제된 서브도메인을 선점하고 장악해 쿠키를 탈취하고 제어권을 얻는 것이다. 위에서 언급한 공격은 이의 변종이다.
react-cookie를 통해 api.example.com의 쿠키의 스코프를 넓히기 위해 domain: .example.com 과 같이 example.com의 서브도메인을 전부 허용하는 코드를 작성하는 경우를 가정하자.
이 경우 evilcode.example.comevilcode.api.example.com 과 같은 도메인의 쿠키를 허용하게 되므로, 공격자가 이를 활용할 수 있게 된다.

SSO 보안: Replay Attack 대응 필요

Replay attack 이란, 공격자가 노출된 사용자의 토큰을 이용해 인증을 받아 들어올 수 있다. 이에 대응하기 위해 토큰의 유효시간을 매우 짧게 주고(약 15분) 유효시간 이전에 꾸준히 재발행하여 공격자가 침입할 시간을 줄이는 것이 도움이 된다.

그 놈의 CORS..

웹 개발자라면 누구나 한번씩 골치를 썩는 CORS가 문제가 될 수 있다. 쿠키는 기본적으로 SameSite로 설정되어 있기 때문에 다른 도메인으로 전송되지 않는다. 이를 다른 도메인에 보내기 위해서는 별도의 설정을 거쳐야한다. 참고 블로그

결론

결정을 모두 내렸으니 이제 코드를 쓰러 가자.

레퍼런스

react-cookie :: 서브 도메인이 다를 때, 같은 쿠키 값 공유하기
sso - 구현 방법, 쿠키 연동
Attacking SSO With Subdomain Takeovers
서로 다른 도메인 간 통합인증(SSO) 구현 방법
SSO 모델과 보안 기술

profile
wannabe dev

0개의 댓글