저번 포스팅에서는 SSO를 조금이나마 더 이해하기위해 JWT와 PKI를 공부하고 각 요소의 역할을 알아보았다 ! 이번 포스팅에서는 SSO에서의 토큰 생성 및 검증 동작 흐름과 이를 더 보안하는 방법을 알아보려한다 !
JWT 토큰의 종류와 목적
SSO 시스템에서 사용되는 JWT (JSON Web Token)은 다양한 목적을 위해 여러 종류의 토큰을 사용한다. 각각의 토큰은 특정한 역할을 담당하며, 이를 통해 사용자의 인증과 권한을 관리한다.
Access 토큰
- 목적: 특정 리소스에 대한 접근 권한을 부여하기 위해 사용
- 유효 기간: 일반적으로 짧으며 (5분~15분), 보안성을 높이기 위해 빠르게 만료되도록 설정
- 사용 위치: 각 SP (Service Provider) 서버에서 사용자 권한을 확인할 때 사용
ID 토큰
- 목적: 사용자 인증 정보를 담고 있으며, SP에서 사용자의 인증 상태를 확인하기 위해 사용
- 유효 기간: Access 토큰보다 길지만, Refresh 토큰보다는 짧음
- 사용 위치: 사용자 정보를 필요로 하는 SP 애플리케이션에서 활용
Refresh 토큰
- 목적: Access 토큰이 만료되었을 때 새로운 Access 토큰을 발급받기 위해 사용
- 유효 기간: 긴 유효 기간을 가지며 (일반적으로 몇 주에서 몇 달), 클라이언트가 장기간 인증된 상태를 유지할 수 있도록 도움
- 사용 위치: 클라이언트가 IDP (Identity Provider)로부터 새로운 Access 토큰을 발급받기 위해 사용
JWT 토큰 생성 및 검증 과정
JWT 토큰은 사용자의 인증 상태를 나타내며, 이를 통해 SP는 사용자의 신원을 확인하고 권한을 부여한다. 이 과정에서 IDP는 토큰을 생성하고, SP는 이를 검증하는 역할을 한다.
토큰 생성 과정
- 사용자 인증 요청: 사용자가 IDP 서버에 로그인 요청
- 사용자 정보 검증: IDP 서버는 사용자 ID 및 비밀번호를 검증하여 사용자를 확인
- JWT 생성: 사용자가 인증되면 IDP 서버는 Access 토큰, ID 토큰, Refresh 토큰을 생성. 이때 각 토큰은 IDP의 비밀 키를 사용해 서명
토큰 검증 과정
- 클라이언트의 요청: 사용자는 SP에 자원 요청을 보냄. 이때 Access 토큰과 ID 토큰이 함께 전송
- JWT 서명 검증: SP는 JWT의 헤더에 포함된 kid 값을 사용해 IDP의 JWKS 엔드포인트에서 적절한 공개 키를 가져옴. 가져온 공개 키를 사용해 서명을 검증
- 페이로드 확인: 서명이 유효하다면 JWT의 페이로드를 확인하여 만료 기간(
exp)이 유효한지, 그리고 필요한 클레임들이 포함되어 있는지를 검증
이제 실제로는 어떤식으로 동작이 흘러가는지 알아보려한다 !!!
밑의 이미지는 내가 그려본 Token기반 SSO의 동작 흐름이다. 아주 간략하게 그려놓은 플로우라 그냥 스쳐지나가듯이 봐도될거같다..!

SSO 플로우에서의 토큰 발급과 갱신
SSO 시스템에서는 토큰을 사용하여 사용자 인증 상태를 관리하고, 필요할 때마다 토큰을 갱신하는 과정을 거친다. 이를 통해 사용자는 여러 서비스에 한 번의 로그인으로 접근할 수 있다.
로그인 및 토큰 발급
- 로그인 시도: 사용자가 SP1에 처음 접근하면, SP1은 인증이 필요하다고 판단하고 IDP로 리다이렉트
- 사용자 인증: 사용자가 IDP에서 자격 증명을 제출하고 인증되면, IDP는 Access 토큰, ID 토큰, Refresh 토큰을 발급한다.
- 토큰 저장: 브라우저는 Access 토큰과 ID 토큰을 HttpOnly 및 Secure 쿠키에 저장한다. Refresh 토큰도 HttpOnly 쿠키에 저장된다.
왜 리다이렉트를 하는걸까 ? SP1이 직접 IDP에 요청하면 안될까 ?
- 리다이렉트를 통해 브라우저가 IDP에 접근하면, IDP는 브라우저의 세션 및 쿠키를 활용해 이미 로그인되어 있는지 확인할 수 있게된다.
- 만약 사용자가 이미 IDP에 로그인한 상태라면, 추가적인 로그인 없이 자동으로 인증 정보를 SP1으로 전달할 수 있는데 이것이 SSO의 핵심이라고 할수있다.
- 리다이렉트를 통해 IDP는 사용자 인증 후 SAML, OAuth, 또는 OIDC 같은 표준 프로토콜을 사용해 SP1으로 인증 결과를 안전하게 전달한다.
- 이 과정에서 JWT, SAML Assertion 같은 토큰이 포함된 응답을 SP1에 전달하며, 이를 통해 SP1은 IDP가 사용자 인증을 완료했음을 신뢰할 수 있다.
자원 접근
- SP1에 접근: 사용자가 SP1에 접근하면, 클라이언트는 Access 토큰과 ID 토큰을 전송한다.
- 토큰 검증: SP1은 토큰의 서명을 검증하고, 페이로드 정보를 확인하여 사용자의 접근을 허용한다.
Access 토큰 만료 및 갱신
- 토큰 만료: Access 토큰이 만료되면, 사용자는 새로운 토큰이 필요하게 된다.
- Refresh 토큰 사용: 클라이언트는 Refresh 토큰을 사용해 IDP에 새로운 Access 토큰과 ID 토큰을 요청한다.
- 갱신된 토큰 발급: IDP는 Refresh 토큰을 검증한 후, 새로운 Access 토큰과 ID 토큰을 발급하여 클라이언트에 전달한다.
로그아웃 처리
- 사용자가 로그아웃을 요청하면, IDP는 서버 측에서 세션 정보를 삭제하고 클라이언트에 저장된 모든 토큰(Access, ID, Refresh)을 폐기한다.
보안 고려 사항 및 베스트 프랙티스
토큰의 보안 저장
- HttpOnly 및 Secure 쿠키 사용: 클라이언트에서 토큰이 탈취되는 것을 방지하기 위해 HttpOnly 쿠키에 저장하고, HTTPS 통신에서만 전송되도록 설정한다.
짧은 Access 토큰 유효 기간
- 짧은 Access 토큰 유효 기간을 설정하여 토큰이 탈취되더라도 악용될 위험을 최소화한다.
Refresh 토큰 보안
- 장기적 사용을 위해 Refresh 토큰의 보안이 중요하다. Refresh 토큰이 탈취되지 않도록 HttpOnly 설정을 사용하며, Refresh 토큰의 사용 횟수를 모니터링하여 비정상적인 사용을 감지한다.
서명 알고리즘 선택
- RSA와 같은 비대칭 서명 알고리즘을 사용하는 것이 HMAC보다 보안성이 높다. IDP와 SP 간의 관계에서 비대칭 서명을 사용하면, SP는 공개 키만으로 JWT를 검증할 수 있어 보안성이 향상된다.
4.5 추가적인 보안 강화 방법
- PKCE (Proof Key for Code Exchange): 특히 공용 클라이언트(예: 모바일 앱)에서는 PKCE를 사용하여 인증 코드 교환 시 중간자 공격을 방지한다.
- IP 허용 목록 설정: 민감한 리소스에 접근할 수 있는 서버의 IP를 허용 목록으로 관리하여, 허가된 IP에서만 접근할 수 있도록 제한한다.
- 로그 모니터링 및 알림: 비정상적인 로그인 시도나 토큰 재발급 요청을 모니터링하고, 의심스러운 활동에 대한 알림을 설정하여 빠르게 대응할 수 있도록 한다.
- CORS 정책 설정: CORS(Cross-Origin Resource Sharing) 정책을 올바르게 설정하여, 신뢰할 수 있는 도메인에서만 리소스에 접근하도록 제한한다.
- 강력한 암호 정책: 사용자 계정의 보안을 위해 IDP에서 강력한 암호 정책을 설정하고, 주기적인 암호 변경을 요구한다.
아직 SSO에 대해 이해하기는 한참 멀었지만 이렇게 조금이나마 공부를 하고서 SSO에 대한 서버를 구현해보려한다. IDP와 SP1, SP2 서버를 구현 할것인데, SP2는 다른 루트 도메인을 사용하는 서버로 구분 지어서 구현할것이다. 위에 언급했듯이 CORS 정책을 설정해 구현해보려한다.
그리고 JWT 토큰 구현을 외부 라이브러리 없이 직접 구현해보려한다. 그렇게 하면 토큰이 만들어지고 서명하고 검증하는 부분을 더 깊게 알수있지않을까 한다. 깃허브에 올려볼 생각입니다 !!