분산 환경에서 RESTful API 인증 강화: JWT 프로토콜 상세 분석

마이크로서비스 아키텍처의 API 보안 과제

마이크로서비스 아키텍처가 적용된 서비스에서 외부에 노출되는 인터페이스는 여러 가지 보안 리스크를 내포하고 있습니다. 특히 공개 API 를 다룰 때 다음과 같은 보안 위협을 고려해야 합니다.

  • 요청의 출처가 권한 있는 자인지 확인
  • 전송 중인 파라미터의 위변조 방지
  • 동일한 요청의 중복 실행 제어 (Idempotency)

이러한 위험으로부터 데이터를 보호하기 위해서는 통신 과정에서 사용자의 신원을 엄격하게 검증해야 합니다. 일반적인 인증 수단으로는 세션 (Session) 과 토큰 (Token) 이 있으며, 현대적인 분산 환경에서는 토큰 기반 방식을 선호합니다. 본 콘텐츠에서는 토큰 인증의 진화 형태인 JWT(JSON Web Token) 의 구조와 활용 방안을 구체적으로 살펴봅니다.

기존 세션 (Session) 기반 인증의 한계

웹 개발 초기부터 표준처럼 사용되어 온 세션 방식은 사용자가 로그인 후 서버가 생성한 세션 아이디를 관리하여 상태를 유지합니다. 서버 측에만 정보가 보관되므로 민감한 데이터를 직접 클라이언트에게 보내지 않아도 된다는 장점이 있으나, 확장과 보안 측면에서 다음과 같은 단점이 존재합니다.

  • 유저 상태 의존성 (Statefulness): 부하 분산 장비가 앞단에 있을 경우, 첫 요청이 노드 A 로 처리되어 세션이 생성되었다면 두 번째 요청은 반드시 노드 A 로 라우팅되어야 합니다. 만약 노드 B 가 처리하면 세션을 찾지 못해 재로그인이 요구됩니다. 이를 해결하려면 세션 공유 레이어가 필요합니다.
  • CSRF 취약성: 브라우저가 자동으로 쿼리 스트링이나 헤더에 세션 정보를 첨부하므로, 악의적인 사이트에서 유효한 세션을 가진 상태에서 조작된 요청을 서버에 보낼 수 있습니다.

토큰 기반 인증과 JWT 의 등장 배경

토큰 인증은 클라이언트가 인증 성공 시 발급받은 문자열을 각 요청마다 서버로 제출하는 방식입니다. 이는 세션 방식의 주요 문제를 해결합니다.

  • 무상태성 (Statelessness): 서버는 클라이언트가 전달한 토큰만으로도 인증 상태를 파악할 수 있어, 특정 서버 인스턴스에 의존하지 않습니다. 공통 데이터베이스나 캐시를 통해 유효성을 검사하거나, 정보 자체가 토큰 안에 담겨 있다면 추가 조회 없이 검증 가능합니다.
  • CORS 및 CSRF 대응: 클라이언트가 명시적으로 인증 정보를 헤더에 기입해야 하므로, 임의의 웹사이트에서 간접적으로 요청을 발생시키기 어렵습니다.

기존 토큰 방식은 일반적으로 생성된 고유 문자열과 사용자 정보를 연결하여 DB 에 저장하다가, 매 요청마다 해당 토큰의 유효성을 다시 조회했습니다. 반면, JWT(JSON Web Token) 은 사용자 정보 자체를 토큰 내에 암호화하여 담고 있으므로 별도의 스토어 조회 과정이 불필요하며 시스템 오버헤드를 줄일 수 있습니다.

JWT 의 구조적 분석

JWT 는 점 (.) 으로 구분된 세 부분으로 구성되며, 각 구간은 Base64Url 인코딩된 문자열입니다.

1. 헤더 (Header)

토큰의 유형과 서명에 사용되는 알고리즘을 정의합니다. 예를 들어 HMAC-SHA256 알고리즘을 사용한 경우에는 다음과 같은 객체 구조를 갖습니다.

{
  "typ": "JWT",
  "alg": "HS256"
}

이 객체는 Base64Url 로 변환되어 토큰의 첫 번째 부분이 됩니다.

2. 페이로드 (Payload)

실제 인증 데이터나 속성 정보를 담는 공간입니다. 표준 정의 클레임 외에 비즈니스 로직에 필요한 정보 (예: 사용자 역할, 조직 코드 등) 를 포함할 수 있지만, 비밀번호나 민감 정보는 절대 넣지 않아야 합니다.

{
  "iss": "example.com",
  "sub": "user_89210",
  "role": "administrator",
  "exp": 1715678900,
  "iat": 1715592500
}
  1. iss: 토큰 발행 주체 식별자
  2. sub: 토큰 대상 사용자 식별자
  3. exp: 토큰 만료 시간 (Unix 타임스탬프 기준)
  4. iat: 토큰 발행 시각 (Unix 타임스탬프 기준)

이 역시 Base64Url 로 변환되어 두 번째 부분에 위치합니다.

3. 서명 (Signature)

위험성이 가장 높은 부분은 토큰의 무결성을 보장하는 서명 부분입니다. 헤더와 페이로드를 점으로 연결한 문자열에 미리 설정된 비밀 키 (Secret Key) 를 사용하여 해시 알고리즘을 적용하여 생성합니다.

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret_key)

생성된 해시값을 Base64Url 로 다시 인코딩하여 세 번째 부분에 배치합니다. 최종적으로 완성된 JWT 는 다음과 같은 형태를 가집니다.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

서명 생성에 사용되는 비밀키는 서버 내부에서만 알 수 있어야 합니다. 만약 클라이언트나 제 3 자가 토큰 내용을 변조한다면, 서버가 동일한 키로 재검증했을 때 계산된 해시값과 기존 서명이 불일치하여 요청이 거절됩니다. 이를 통해 전송 중의 위변조 공격을 효과적으로 방어할 수 있습니다.

태그: jwt RESTful-API microservices Security authentication

5월 23일 16:27에 게시됨