Lionel Blog

The road is under your feet, the heart looks to the distance

최근 마이크로서비스 보안 관련 기존 아이디어를 추가로 요약하고 정리하여, 이전 글에서 명확하지 않았던 부분, 예를 들어 서비스 간 인증 및 권한 부여, 서비스 호출 체인에서 사용자 신원 전달 등을 명확히 했습니다.

이 시리즈 글에서는 마이크로서비스 보안을 세 부분으로 나누어 체계적으로 설명할 것입니다: 사용자 접근 인증 및 권한 부여, 서비스 간 인증 및 권한 부여, 외부 시스템 접근 제어.

목차

{:.no_toc}

  • 목차 {:toc}

서론

마이크로서비스 아키텍처의 도입은 소프트웨어 애플리케이션에 많은 이점을 가져다주었습니다: 소규모 개발 팀, 개발 주기 단축, 언어 선택 유연성, 서비스 확장성 향상 등. 동시에 분산 시스템의 많은 복잡한 문제도 도입했습니다. 그중 하나의 과제는 마이크로서비스 아키텍처에서 유연하고 안전하며 효율적인 인증 및 권한 부여 솔루션을 구현하는 방법입니다.

기존 모놀리식 애플리케이션에 비해 마이크로서비스 아키텍처의 인증 및 권한 부여는 사용자 마이크로서비스 애플리케이션 접근, 타사 애플리케이션 마이크로서비스 애플리케이션 접근, 애플리케이션 내 여러 마이크로서비스 간 상호 접근 등 더 복잡한 시나리오를 포함하며, 애플리케이션의 보안을 보장하기 위해 각 시나리오의 인증 및 권한 부여 솔루션을 고려해야 합니다. 이 블로그 시리즈에서는 이 문제에 대해 비교적 완전한 탐구를 진행할 것입니다. 마이크로서비스 인증 및 권한 부여와 관련된 세 가지 시나리오

마이크로서비스 인증 및 권한 부여와 관련된 세 가지 시나리오

사용자 인증 및 권한 부여

사용자 신원 인증

완전한 마이크로서비스 애플리케이션은 여러 개의 독립적인 마이크로서비스 프로세스로 구성되며, 각 마이크로서비스에 대한 접근은 사용자 인증이 필요합니다. 사용자 인증 작업을 각 마이크로서비스에 배치하면 다음과 같은 문제가 발생합니다.

  • 이러한 공통 로직을 각 마이크로서비스에서 반복적으로 구현해야 합니다. 코드 라이브러리를 사용하여 일부 코드를 재사용할 수 있지만, 이는 모든 마이크로서비스가 특정 코드 라이브러리 및 해당 버전에 의존하게 되어 마이크로서비스 언어/프레임워크 선택의 유연성에 영향을 미칩니다.
  • 인증 및 권한 부여의 공통 로직을 마이크로서비스 구현에 배치하는 것은 단일 책임 원칙에 위배되며, 개발자는 마이크로서비스 자체의 비즈니스 로직에 집중해야 합니다.
  • 사용자는 시스템의 다른 서비스에 접근하기 위해 각각 로그인해야 합니다.

마이크로서비스 아키텍처에서는 API 게이트웨이를 외부 서비스 제공의 진입점으로 사용하므로, API 게이트웨이에서 통합 사용자 인증을 제공할 수 있습니다. 사용자는 한 번만 로그인하면 시스템의 모든 마이크로서비스가 제공하는 서비스에 접근할 수 있습니다.

사용자 상태 유지

HTTP는 무상태 프로토콜이므로 서버 입장에서는 사용자의 각 HTTP 요청이 서로 독립적입니다. 인터넷은 거대한 분산 시스템이며, HTTP 프로토콜은 인터넷의 중요한 프로토콜로서 설계 초기부터 대량의 애플리케이션 접근 효율성 문제를 고려해야 했습니다. 무상태는 서버가 클라이언트의 요청을 필요에 따라 클러스터의 어떤 노드로든 보낼 수 있음을 의미하며, HTTP의 무상태 설계는 로드 밸런싱에 명확한 이점을 제공합니다. 상태가 없으므로 사용자 요청은 임의의 서버로 분산될 수 있으며, 애플리케이션은 사용자에게 가까운 네트워크 에지에 캐시 서버를 배포할 수도 있습니다. 뉴스 웹 페이지 탐색과 같이 신원 인증이 필요 없는 서비스의 경우 아무런 문제가 없습니다. 그러나 HTTP가 엔터프라이즈 애플리케이션의 사실상 표준이 된 후, 엔터프라이즈 애플리케이션은 더 엄격한 권한 제어를 위해 사용자 로그인 상태와 신원을 저장해야 합니다. 따라서 HTTP 프로토콜을 기반으로 사용자 로그인 상태를 유지하는 방법을 채택하여 사용자가 요청을 보낼 때마다 인증할 필요가 없도록 해야 합니다.

전통적인 방식은 서버 측에서 쿠키를 사용하여 사용자 상태를 저장하는 것이었습니다. 서버가 상태를 가지므로 서버의 수평 확장에 영향을 미쳤습니다. 마이크로서비스 아키텍처에서는 토큰을 사용하여 사용자 로그인 상태를 기록하는 것이 좋습니다.

토큰과 세션의 주요 차이점은 저장 위치가 다르다는 것입니다. 세션은 서버에 중앙 집중식으로 저장되는 반면, 토큰은 사용자가 직접 소유하며 일반적으로 쿠키 형태로 브라우저에 저장됩니다. 토큰에는 사용자 신원 정보가 저장되어 있으며, 요청할 때마다 서버로 전송되어 서버가 접근자의 신원을 판단하고 요청된 리소스에 대한 접근 권한이 있는지 판단할 수 있도록 합니다.

토큰은 사용자 신원을 나타내므로, 요청자 또는 제3자에 의한 변조를 방지하기 위해 내용을 암호화해야 합니다. JWT(Json Web Token)는 토큰 형식(RFC 7519)을 정의하는 개방형 표준으로, 토큰의 내용, 암호화 방식, 그리고 다양한 언어의 라이브러리를 제공합니다.

JWT 토큰의 구조는 매우 간단하며, 세 부분으로 구성됩니다.

  • 헤더
    헤더에는 유형(고정 값 JWT)과 JWT에서 사용하는 해시 알고리즘이 포함됩니다.
{
  "alg": "HS256",
  "typ": "JWT"
}
  • 페이로드
    발행자, 만료 시간, 사용자 이름 등 표준 정보가 포함되며, 사용자 역할, 사용자 정의 정보도 추가할 수 있습니다.
{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}
  • 서명
    토큰 발행자의 서명으로, 클라이언트가 토큰 발행자의 신원을 확인하고 서버가 토큰 변조를 방지하는 데 사용됩니다. 서명 알고리즘
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

이 세 부분은 Base64로 인코딩된 후 조합되어 최종적으로 클라이언트에 반환되는 토큰 문자열이 되며, 각 부분은 “.“으로 구분됩니다. 아래 그림은 위 예시에서 최종적으로 형성된 토큰입니다. xx 토큰을 사용하여 사용자 인증을 수행하면 서버는 더 이상 사용자 상태를 저장하지 않으며, 클라이언트는 요청할 때마다 토큰을 서버로 전송하여 신원 확인을 수행해야 합니다. 토큰 전송 방식은 rfc6750에 규정되어 있으며, Authorization: Bearer HTTP 헤더를 사용하여 전송합니다.

Authorization: Bearer mF_9.B5f-4.1JqM

토큰 방식을 사용한 사용자 인증의 기본 흐름은 다음과 같습니다.

  1. 사용자가 사용자 이름, 비밀번호 등 인증 정보를 입력하고 서버에 로그인 요청을 보냅니다.
  2. 서버는 사용자 로그인 정보를 확인하고 JWT 토큰을 생성합니다.
  3. 서버는 토큰을 클라이언트에 반환하고, 클라이언트는 이를 로컬에 저장합니다(일반적으로 쿠키 형태로 저장).
  4. 클라이언트는 서버에 접근 요청을 보내고, 요청에 이전에 발급받은 토큰을 포함합니다.
  5. 서버는 토큰을 확인하여 사용자의 신원과 리소스 접근 권한을 확인하고, 해당 처리를 수행합니다(접근 거부 또는 허용).
토큰을 사용한 사용자 인증 흐름도

단일 로그인 구현

단일 로그인(Single Sign-On, SSO)의 개념은 간단합니다. 사용자가 애플리케이션에 한 번만 로그인하면 애플리케이션의 모든 마이크로서비스에 접근할 수 있다는 것입니다. API 게이트웨이는 클라이언트가 마이크로서비스 애플리케이션에 접근하는 진입점을 제공하고, 토큰은 무상태 사용자 인증을 구현합니다. 이 두 기술을 결합하여 마이크로서비스 애플리케이션에 단일 로그인 솔루션을 구현할 수 있습니다.

사용자 인증 흐름은 토큰 방식 인증의 기본 흐름과 유사하며, 외부 요청의 진입점으로 API 게이트웨이가 추가되었다는 점이 다릅니다.

사용자 로그인

  1. 클라이언트가 API 게이트웨이로 로그인 요청을 보냅니다.
  2. API 게이트웨이는 로그인 요청을 보안 서비스로 전달합니다.
  3. 보안 서비스는 사용자 신원을 확인하고 토큰을 발급합니다.

사용자 요청

  1. 클라이언트 요청이 API 게이트웨이로 전송됩니다.
  2. API 게이트웨이는 보안 서비스를 호출하여 요청의 토큰을 확인하고 사용자 신원을 검사합니다.
  3. 요청에 토큰이 없거나, 토큰이 만료되었거나, 토큰 확인이 유효하지 않으면 사용자 요청을 거부합니다.
  4. 보안 서비스는 사용자가 해당 작업 권한을 가지고 있는지 확인합니다(선택 사항, 다음 소절 참조).
  5. 사용자가 해당 작업 권한을 가지고 있으면 요청을 백엔드 비즈니스 서비스로 전송하고, 그렇지 않으면 사용자 요청을 거부합니다. API 게이트웨이를 사용하여 마이크로서비스 애플리케이션 SSO 구현
API 게이트웨이와 토큰을 사용하여 마이크로서비스 애플리케이션의 단일 로그인 구현

사용자 권한 제어

사용자 권한 제어에는 API 게이트웨이에서 통합 처리하는 방법과 각 마이크로서비스에서 개별적으로 처리하는 두 가지 방법이 있습니다.

API 게이트웨이에서 통합 권한 제어

클라이언트가 보내는 HTTP 요청에는 요청된 리소스와 HTTP 메서드가 포함됩니다. 시스템이 REST 규약을 따르고 URI 리소스 방식으로 접근 대상을 모델링한다면, API 게이트웨이는 요청에서 직접 접근할 리소스와 수행할 작업을 추출하여 보안 서비스를 호출하여 권한을 판단할 수 있습니다. 판단 결과에 따라 사용자가 해당 리소스에 대한 작업을 수행할 권한이 있는지 결정하고, 백엔드 비즈니스 서비스로 전달합니다.

시스템에 세 가지 역할이 있다고 가정합니다.

  • order_manager: 주문을 조회, 생성, 수정, 삭제할 수 있습니다.
  • order_editor: 주문을 조회, 생성, 수정할 수 있습니다.
  • order_inspector: 주문을 조회만 할 수 있습니다.

이러한 역할의 리소스 작업 권한은 아래 표와 같이 HTTP Verb에 매핑될 수 있습니다.

역할리소스동사
order_manager/orders‘GET’ ‘POST’ ‘PUT’ ‘DELETE’
order_editor/orders‘GET’ ‘POST’ ‘PUT’
order_inspector/orders‘GET’

이 구현 방식은 API 게이트웨이에서 권한 부여 로직을 통합 처리하므로, 각 마이크로서비스는 사용자 권한 부여를 고려할 필요 없이 비즈니스 로직만 처리하면 되어 각 마이크로서비스의 구현을 단순화합니다.

각 마이크로서비스에서 개별적으로 권한 제어

마이크로서비스가 접근 대상을 REST 규약에 엄격하게 따르지 않거나, 애플리케이션이 더 세분화된 권한 제어를 필요로 하는 경우, 마이크로서비스 내에서 사용자 권한을 개별적으로 판단하고 처리해야 합니다. 이 경우 마이크로서비스의 권한 제어가 더 유연하지만, 각 마이크로서비스는 사용자 권한 부여 데이터를 개별적으로 유지 관리해야 하므로 구현이 더 복잡해집니다.

마이크로서비스가 권한 판단을 할 때 사용자 신원 정보가 필요하므로, 이 솔루션에서 처리해야 할 또 다른 문제는 로그인한 사용자 정보를 API 게이트웨이에서 마이크로서비스로 전달하는 방법입니다. HTTP 기반이라면 HTTP 헤더를 사용할 수 있으며, 다른 프로토콜이라면 메시지 본문에 사용자 신원 관련 필드를 추가해야 합니다.