RSS
보안

서비스 간 인증 키 관리: 공유 토큰부터 mTLS까지 규모별 4단계

작성자
듀오랩스 대표·8분 읽기

예를 들어 직원 열 명 남짓한 쇼핑몰 운영사를 생각해 봅시다. 주문 관리 화면이 재고 서버에서 수량을 읽고, 고객센터 도구가 주문 서버에서 문의 내역을 가져오고, 사내 챗봇이 외부 AI API를 부릅니다. 연결은 세 가닥이고 키도 세 개입니다. 그런데 이 키들을 비밀 관리 도구에 옮겨 적으려고 표를 그려 보면 칸이 다섯 개 나옵니다.

값은 셋인데 칸이 다섯인 이유를 따라가 보면, 서비스끼리 서로를 어떻게 믿게 만드는지에 대한 질문 전체가 딸려 나옵니다.

키가 길면 안전하다는 오해

흔히 서비스 간 키의 안전을 길이와 무작위성으로 판단합니다. openssl rand -hex 32로 만든 64자 토큰이면 256비트이고, 영문 대소문자와 숫자 32자짜리 키도 약 190비트입니다. 어느 쪽이든 추측해서 맞히는 길은 없습니다. 여기까지는 맞는 이야기입니다.

어긋나는 곳은 그다음입니다. 위 쇼핑몰의 재고 토큰은 재고 서버와 주문 관리 화면이 같은 값을 하나씩 들고 서로 맞춰 보는 공유 비밀입니다. 그래서 비밀 관리 도구에도 서비스마다 한 칸씩, 같은 값이 두 번 들어갑니다. 문의 토큰도 마찬가지입니다. 세 개가 다섯 칸이 되는 이유입니다.

칸만 늘어나는 것도 아닙니다. 한 값의 사본을 세어 보면 비밀 관리 도구, CI의 시크릿, 호스팅 업체의 환경 변수, 배포 때 서버에 써지는 env 파일, 실행 중인 컨테이너의 환경까지 다섯 곳이 됩니다. 키는 길이로 깨지지 않습니다. 이 다섯 곳 가운데 하나에서 샙니다.

공유 비밀의 이름을 양쪽에 똑같이 두는 이유

공유 비밀에서 가장 흔하게 생기는 실수는 이름입니다. 재고 서버에서는 INVENTORY_SYNC_TOKEN이라 부르고, 주문 관리 화면에서는 어디로 가는 열쇠인지 드러내려고 STOCK_API_TOKEN이라 붙이는 식입니다. 각자의 자리에서 보면 둘 다 읽기 좋은 이름입니다.

문제는 비밀 관리 도구에서 두 칸을 나란히 볼 때 생깁니다. 같은 값이라는 단서가 어디에도 없습니다. 키를 갈 때 한쪽만 바꾸면 연결이 끊기고, 왜 끊겼는지는 이름만으로 찾을 수 없습니다. 저는 공유 비밀이라면 양쪽 이름을 똑같이 두는 쪽이 맞다고 봅니다. 출처를 이름에 담고 싶은 마음은 주석으로 채우면 됩니다.

이름을 맞추는 순서에도 함정이 있습니다. 옛 이름을 먼저 지우면 배포가 도는 사이에 연결이 빕니다. 새 이름으로 넣고, 배포하고, 실제 토큰으로 불러 본 다음에 옛 이름을 지우는 순서가 안전합니다.

접두사가 붙은 키와 맨 난수 토큰

같은 회사 안에서도 키의 모양은 제각각입니다. 외부 AI API의 키는 보통 sk-처럼 발급처가 붙인 접두사로 시작하고, 사내에서 직접 만든 공유 토큰은 접두사 없는 난수입니다.

차이는 누가 발급하느냐에서 옵니다. 여러 사용자에게 키를 나눠 주는 발급처는 대개 원문을 저장하지 않고, 앞 몇 자와 해시만 남겨 둡니다. 요청이 들어오면 앞자리로 행 하나를 찾고 해시를 대조하는 방식입니다. 접두사는 로그에 찍혀 나온 키가 무슨 키인지 바로 알아보게 해 주기도 합니다. GitHub가 2021년에 토큰 형식을 ghp_, gho_ 같은 접두사로 바꾼 것도 비밀 스캐너가 저장소에 섞여 들어간 토큰을 오탐 없이 찾게 하려는 이유였습니다(GitHub 블로그).

두 서비스가 같은 값을 그대로 비교하기만 하는 공유 토큰에는 접두사가 할 일이 거의 없습니다. 다만 유출을 알아보기 쉽게 하려고 붙이는 것은 충분히 의미가 있고, 새로 만드는 토큰이라면 붙여 둘 만하다고 봅니다.

서비스끼리 믿는 방식의 네 단계

"보통 이렇게 운영하나"에 대한 답을 하자면, 이 규모에서는 흔한 방식이지만 업계가 올라가는 길도 분명히 정해져 있습니다. 서비스끼리 서로를 믿는 방식은 대략 네 단계로 나뉜다고 봅니다.

단계 서비스끼리 인증 비밀이 사는 곳
1. 네트워크를 믿는다 없음. 같은 망 안에 있으면 우리 편 서버의 env 파일
2. 고정 키 API 키나 공유 토큰을 헤더에 실음 비밀 관리 도구, CI, 호스팅 업체에서 주입
3. 짧게 사는 신원 몇 분에서 몇 시간짜리 토큰을 자동 발급(OIDC, 클라우드 IAM) 사람이 키를 만지지 않음
4. 모든 연결을 증명 서비스마다 인증서를 들고 mTLS로 서로 확인 인증서가 자동으로 교체됨

예로 든 쇼핑몰은 2단계에 있습니다. 실로 이어 놓은 느낌이 드는 것은 2단계의 본질입니다. 고정 키는 양쪽이 같은 비밀을 나눠 든 것이고, 그 비밀은 누군가 바꾸기 전까지 계속 유효합니다.

3단계는 그 실을 저절로 끊어졌다 다시 이어지는 실로 바꿉니다. GitHub Actions가 클라우드에 배포할 때 저장된 키 대신 OIDC 토큰을 쓰게 하는 것이 대표적입니다(GitHub 문서). 작업이 도는 동안만 유효한 토큰이라 훔쳐 가도 곧 쓸모가 없어집니다.

4단계는 네트워크 위치를 아예 믿지 않는 쪽입니다. 구글이 사내망 경계를 걷어 내고 모든 접근을 기기와 사용자 신원으로 판단하게 바꾼 BeyondCorp가 이 흐름을 연 사례로 자주 꼽힙니다. NIST는 이를 SP 800-207 제로 트러스트 아키텍처로 정리했습니다. 서비스끼리의 신원을 표준으로 다루는 SPIFFE 같은 프로젝트도 이 단계에 속합니다.

1단계에서 2단계로는 반드시 올라가야 한다고 봅니다. "같은 망 안이니 안전하다"는 가정은 망 안의 무엇 하나가 뚫리는 순간 무너지고, 침입이 옆으로 번지는 길도 대개 그쪽입니다. 반대로 3단계와 4단계는 열 명 규모 회사의 서비스 몇 개에는 과하다고 봅니다. 서비스 메시를 들이는 순간 지키는 이득보다 관리하는 일이 커집니다.

고정 키 단계를 제대로 쓰는 조건

그래서 작은 팀의 현실적인 목표는 위 단계로 서둘러 가는 것이 아니라 2단계를 제대로 쓰는 것입니다. 제가 보는 조건은 넷입니다.

첫째는 범위입니다. 키 하나가 할 수 있는 일이 좁아야 합니다. 주문 관리 화면이 재고를 읽기만 한다면 재고 토큰도 읽기만 열어야 하고, 쓰기용 토큰을 같이 쓰면 안 됩니다. 외부 API 키라면 사용처별로 따로 발급받고 호출 상한을 걸어 둡니다.

둘째는 정본입니다. 정본은 한 곳이고 나머지는 거기서 내려받은 사본이어야 합니다. 급할 때는 배포처에 먼저 넣고 비밀 관리 도구에 나중에 옮겨 적기 쉬운데, 그러면 두 곳 중 무엇이 맞는지 판단할 근거가 사라집니다. 대조 도구가 수정 시각만 비교한다면 더 헷갈립니다. 나중에 옮겨 적은 쪽이 더 최신으로 보이기 때문입니다.

셋째는 지도입니다. 키마다 어디에 사본이 있고, 갈 때 어떤 순서로 바꾸고, 새면 무엇이 보이는지가 문서 한 장에 적혀 있어야 합니다.

넷째는 감지입니다. 틀린 토큰으로 들어온 요청을 세어 두면, 키가 샌 뒤 누군가 시험해 보는 순간이 숫자로 드러납니다.

3단계로 가는 가장 가까운 길

이미 Cloudflare Access 같은 접근 관리 도구를 쓰고 있다면 3단계에 가까운 선택지가 손에 있습니다. Access에는 사람 대신 서비스가 통과하는 서비스 토큰이 있어서, 키를 Cloudflare가 관리하고, 대시보드에서 끊고, 만료일을 걸 수 있습니다. 서비스끼리 붙는 길을 Access를 지나도록 바꿔야 하므로 작은 공사는 아니지만, 2단계의 조건을 갖춘 다음 단계로는 가장 부담이 적은 편입니다.

여기서부터는 회사마다 판단이 갈립니다. 다만 순서는 분명하다고 봅니다. 키를 몇 개 쓰는지보다, 어느 키가 어디로 이어지는지 모르는 상태가 더 위험합니다. 키 지도부터 그리는 것이 먼저입니다.

마지막 수정:

공유하실 때는 출처(Duolabs)와 원문 주소를 표시해 주세요.