-
어떤 방식으로 액세스 토큰을 제공해야 할까: JWT, 팬텀, 아니면 분리?카테고리 없음 2024. 12. 1. 20:13
How Should You Serve Your Access Tokens: JWTs, Phantom, or Split?
https://curity.io/blog/how-should-you-serve-your-access-tokens-jwts-phantom-or-split/
OAuth는 권한 부여 프레임워크입니다. 이를 통해 클라이언트가 액세스 토큰이라는 디지털 신원을 사용하여 사용자의 리소스에 접근할 수 있습니다. 이러한 액세스 토큰을 적절히 설계하는 것은 시스템 보안에 필수적이지만, 종종 간과되는 경우가 많습니다. 개발자들은 OAuth나 OpenID Connect 흐름을 올바르게 구현하는 데 초점을 맞추는 경향이 있습니다. 물론 보안 흐름은 중요하지만, 이는 액세스 토큰 설계에 대한 세심한 주의와 함께 보완되어야 합니다.
액세스 토큰은 여러 방식으로 분류될 수 있습니다. 예를 들어, "Bearer 토큰 vs. Proof-of-Possession 토큰" 분류는 특정 토큰을 사용할 수 있는 클라이언트를 기준으로 한 것입니다. 또 다른 분류는 토큰의 형식에 따라 나뉘는데, 참조형(불투명) 토큰과 값형 토큰으로 나눌 수 있습니다. 후자는 OAuth 세계에서 보통 JSON 웹 토큰(JWT)으로 표현됩니다.
불투명 토큰은 말 그대로 불투명한 문자열로, 클라이언트에게 아무 의미가 없습니다. 단순히 리소스에 접근하는 데 사용되며, 그 외의 용도로는 유용하지 않습니다. 리소스 서버와 같은 수신자는 액세스 토큰을 받으면 토큰을 검증하고, 권한 부여 결정을 내리기 위해 관련 데이터를 가져와야 합니다. 불투명 토큰의 경우, 수신자는 발급자에게 요청을 보내 토큰을 검증하고 데이터를 가져와야 합니다. 반면 값형 토큰(JWT와 같은 경우)을 사용하면, 수신자는 이미 토큰 내에 모든 정보를 포함하고 있어 발급자에게 별도로 요청하지 않고도 권한 부여를 수행할 수 있습니다. 이러한 이유로 JWT(정확히는 JSON 웹 서명, JWS)가 OAuth 세계에서 인기를 끌고 있습니다.
The Problem of By-Value Tokens
값형 토큰은 이를 수신하는 API에 유용하지만, 본질적인 문제를 가지고 있습니다. 토큰이 암호화되지 않은 경우(대부분의 경우 암호화는 자원과 관리 측면에서 비용이 높기 때문에 이루어지지 않음), JWT의 내용은 이를 소유한 누구나 읽을 수 있습니다. 이는 토큰이 사용자에 대한 정보를 유출할 가능성이 있다는 것을 의미하며, 특히 토큰에 개인정보(PII: Personally Identifiable Information)가 포함되어 있다면 그 심각성이 커질 수 있습니다.
예를 들어, 헬스케어 서비스에서 사용자의 주민등록번호나 민감한 건강 정보를 액세스 토큰에 포함한다고 가정해 봅시다. 이 정보는 JWT를 가진 누구에게나 읽힐 수 있습니다. 이 경우, 악의적인 행위자가 JWT를 가로채는 경우일 수도 있고, 사용자 정보를 수집하는 정당한 OAuth 클라이언트일 수도 있습니다.
액세스 토큰은 API(리소스 서버)를 위한 것이며, 이 토큰에 포함된 데이터는 API가 권한 부여 결정을 내리는 데 필요한 데이터입니다. 하지만 이 데이터는 OAuth 클라이언트에게도 노출되므로, 통합자들이 이를 애플리케이션에서 자신의 목적으로 사용하기 시작하는 경우가 많습니다. 이로 인해 액세스 토큰의 내용이 API 계약의 일부가 되어, 더 이상 이를 자유롭게 변경할 수 없게 됩니다. 예를 들어, 토큰에서 클레임을 제거하거나 토큰 길이를 변경하는 것은 API와 통합된 애플리케이션을 중단시킬 수 있습니다. 이는 고객의 비즈니스에 원하지 않는 피해를 줄 수 있기 때문에 바람직하지 않은 상황입니다.
PII뿐만 아니라, 액세스 토큰의 클레임은 인프라와 관련된 정보를 노출할 수도 있습니다. 이러한 데이터는 해커가 방어 체계를 뚫는 데 악용할 수 있는 정보가 될 수 있습니다.
이러한 문제를 고려할 때, 값형 토큰은 공개적으로 인터넷에 노출되지 않는 내부 인프라에서만 사용해야 합니다. 불투명 토큰은 쿠키에 토큰을 저장할 때도 더 나은 선택이 될 수 있습니다(예: 웹 애플리케이션이나 토큰 핸들러를 사용하는 SPA). 이는 브라우저 크기 제한 내에 맞추기 쉽기 때문입니다. 그럼에도 불구하고 JWT의 편리함 때문에 이를 포기하기는 어렵습니다. 이러한 상황에서 **팬텀 토큰(phantom token)**과 **분리 토큰(split token)**이 도움이 됩니다.
The Phantom Token
팬텀 토큰(Phantom Token) 패턴은 불투명 토큰과 값형 토큰의 장점을 결합한 방식입니다. 이 패턴에서는 권한 부여 서버가 OAuth 클라이언트에 불투명 토큰을 발급하여 토큰으로부터 정보가 유출되는 것을 방지합니다. 클라이언트가 API를 호출하면, API 게이트웨이가 개입합니다(모든 들어오는 트래픽이 API 게이트웨이를 거치도록 하는 것이 모범 사례). 게이트웨이는 application/jwt 미디어 타입을 사용하여 토큰 정보를 조회(Token Introspection)합니다.
조회 결과, 게이트웨이는 불투명 토큰에 해당하는 JWT를 받게 되고, 요청을 처리하는 다운스트림 서비스로 이 JWT를 전달합니다. 이 덕분에 요청을 처리하는 모든 서비스는 JWT를 사용하여 작업하게 됩니다. 서비스는 JWT의 내용을 활용하여 인증 결정을 수행할 수 있으며, 불투명 토큰의 데이터를 가져오기 위해 권한 부여 서버를 추가로 호출할 필요가 없습니다.
조회 과정은 API 게이트웨이에서 권한 부여 서버로의 빠른 로컬 호출로 이루어지며, 동일한 액세스 토큰에 대한 후속 요청에서는 결과를 캐싱하므로 호출이 자주 발생하지 않습니다. 이러한 접근 방식은 이 패턴의 성능을 더욱 향상시킵니다.
The Split Token
분리 토큰(Split Token) 패턴은 앞서 설명한 팬텀 토큰과 유사한 점이 많으며, 결과적으로 동일한 목적을 달성합니다. 즉, OAuth 클라이언트는 불투명 토큰을 사용하고, API의 서비스들은 JWT를 처리하게 됩니다. 그러나 이 패턴에서는 API 게이트웨이가 JWT를 얻기 위해 조회(Introspection) 엔드포인트를 호출할 필요가 없습니다.
권한 부여 서버가 JWT를 발급할 때, JWT를 두 부분으로 나눕니다. 첫 번째 부분은 JWT의 헤더와 페이로드로 구성되며, 두 번째 부분은 토큰의 서명(signature)입니다. 클라이언트는 서명 부분만 반환받아 불투명 토큰으로 사용하고, 헤더와 페이로드는 API 게이트웨이로 전송되어 캐싱됩니다. 이때 서명의 해시값이 캐시 키로 사용됩니다.
클라이언트가 API를 호출할 때, 서명 부분을 전송합니다. API 게이트웨이는 서명 부분을 해싱하고, 해당 해시를 키로 사용해 캐시에서 일치하는 헤더와 페이로드를 검색합니다. 찾으면 이 부분들을 다시 결합하여 JWT를 생성합니다. 이렇게 JWT를 재구성하기 때문에 권한 부여 서버에 추가적으로 접근할 필요가 없습니다.
어떤 방식을 언제 사용할 것인가
여기에서 설명한 접근 방식을 언제 사용해야 할지 고민된다면, 결정 방법은 간단합니다. 토큰이 인프라를 벗어난다면 팬텀 토큰 방식을 사용하세요. 이는 퍼블릭 인터넷에서 API에 연결하는 OAuth 클라이언트(자사 클라이언트든 서드파티 클라이언트든 관계없이)가 있을 때 기본적으로 선택해야 하는 방식입니다. 아주 간단합니다.다음과 같은 상황이라면 분리 토큰(Split Token) 방식을 고려해야 합니다:
- 분산된 API 게이트웨이를 사용하는 경우
매우 분산된 API 게이트웨이를 사용하는 경우, 팬텀 토큰 방식을 사용할 때 요청이 토큰 조회(Introspection)를 기다려야 할 수 있습니다. 게이트웨이가 권한 부여 서버의 응답을 캐시하더라도, 새로운 요청마다 게이트웨이의 다른 인스턴스나 클러스터로 전달될 수 있기 때문입니다. - 지연 시간이 시스템에서 매우 중요한 경우
시스템의 지연 시간을 최소화하고 싶고, API 게이트웨이가 권한 부여 서버의 조회 엔드포인트에 접근하지 않도록 하고 싶다면 분리 토큰 방식을 사용할 수 있습니다. 게이트웨이가 JWT 토큰을 캐시하더라도 조회 과정 없이 동작할 수 있습니다. - 보안상의 이유로 JWT 액세스 토큰을 API 게이트웨이의 캐시에 저장하고 싶지 않은 경우
분리 토큰 방식에서는 게이트웨이의 캐시에 JWT 전체가 저장되지 않습니다. 캐시는 헤더와 페이로드, 서명 해시값만 저장하기 때문에, 캐시가 침해되더라도 공격자는 API를 악용할 수 없습니다.
결론적으로,
- 팬텀 토큰: 토큰이 외부로 나가는 모든 경우에 기본 옵션으로 사용.
- 분리 토큰: 분산 환경, 낮은 지연 시간 요구, 보안 강화가 필요한 경우에 적합.
주의 사항(Caveats)
모든 솔루션은 단점이 있기 마련이며, 아키텍처에서 사용하는 기능, 패턴, 접근 방식의 한계를 이해하는 것이 중요합니다. 팬텀 토큰을 사용하기로 결정하고 값형 토큰을 직접 사용하는 대신 이 방식을 채택하면, 시스템은 다시 권한 부여 서버의 가용성에 의존하게 됩니다. API 게이트웨이는 불투명 토큰을 조회(Introspect)하기 위해 권한 부여 서버에 요청해야 합니다. 물론, 모든 서비스가 자체적으로 토큰을 검증해야 하는 상황에 비해, 권한 부여 서버로의 트래픽은 훨씬 적습니다.캐시 사용 시 장단점
API 게이트웨이는 캐시를 사용해 권한 부여 서버로의 트래픽을 더 줄일 수 있습니다. 특히, 동일한 토큰으로 여러 요청을 보내는 클라이언트가 있는 시스템에서는 캐시가 유용합니다. 하지만 캐시에는 항상 일반적인 문제들이 따릅니다.- 캐시 무효화: 캐시를 올바르게 무효화해야 합니다.
- 동기화: 클러스터나 데이터 센터 간의 캐시를 동기화해야 할 수도 있습니다.
- 토큰 폐기 처리: 시스템에서 토큰을 폐기하는 경우, 캐시는 가능한 한 빠르게 무효화되어야 하며, 폐기된 토큰이 API로 요청을 보낼 수 없도록 해야 합니다.
분리 토큰 방식의 특성
분리 토큰 방식은 분산 캐시를 필요로 하기 때문에, 캐시 문제는 이 접근 방식에 내재되어 있습니다.또한, 토큰 생성 시 캐시를 효과적으로 채워야 하며, 그렇지 않으면 캐시 누락(cache miss)으로 인해 API 요청이 실패할 수 있습니다.
공통 보안 고려 사항
어느 방식을 사용하든, JWT 액세스 토큰을 수신하는 API 서비스는 제로 트러스트(zero-trust) 접근 방식을 구현하고, 보안 모범 사례에 따라 토큰을 검증해야 합니다.이는 특히 분리 토큰 방식에서 중요한데, 캐시 중독(cache poisoning) 공격(서명에 대해 다른 헤더와 페이로드를 주입하려는 시도)을 방지하려면 토큰 검증이 철저해야 합니다.
반응형 - 분산된 API 게이트웨이를 사용하는 경우