18장. 인증, 권한, 테넌시 설계
18장. 인증, 권한, 테넌시 설계

로그인만 붙였다고 안전해지지는 않는다
웹 서비스를 만들 때 인증은 보통 "로그인 기능"으로 먼저 떠오른다. 사용자가 이메일과 비밀번호, 혹은 소셜 로그인을 통해 들어오면 일단 문은 열리는 셈이다. 그런데 LLM 서비스에서는 그다음이 진짜 시작이다. 누가 들어왔는지 확인하는 것만으로는 충분하지 않다. 그 사용자가 무엇을 볼 수 있는지, 무엇을 실행할 수 있는지, 그리고 어느 조직의 데이터 안에서만 움직여야 하는지를 끝까지 추적해야 한다.
이 장의 핵심은 간단하다. AI 기능이 붙는 순간 권한 실수는 단순 버그가 아니라 데이터 유출 사고가 된다.
기존 CRUD 서비스에서는 권한 체크가 하나 빠져도 한 화면이 잘못 보이는 정도로 끝날 수 있다. 하지만 LLM 서비스는 다르다. 사용자의 질문 하나가 문서 검색, 대화 이력 조회, 파일 분석, 외부 API 호출, 백그라운드 작업 생성으로 번질 수 있다. 한 번만 경계가 무너지면 모델은 사람보다 훨씬 빠른 속도로 많은 데이터를 훑고, 요약하고, 정리해서, 심지어 보기 좋게 포장해서 내보낸다. 사고가 더 빨리, 더 크게, 더 조용하게 난다.
그래서 인증, 권한, 테넌시는 부가 기능이 아니라 LLM 서비스의 골격이다.
인증, 권한, 테넌시는 서로 다른 문제다
이 세 단어는 자주 한 묶음으로 말되지만, 실제 설계에서는 반드시 분리해서 생각해야 한다.
인증은 "당신이 누구인가"를 확인하는 과정이다. 권한은 "당신이 무엇을 할 수 있는가"를 결정하는 규칙이다. 테넌시는 "당신이 어느 조직, 어느 고객, 어느 데이터 경계 안에 속하는가"를 고정하는 장치다. API 보안 사고의 상당수는 익명 공격보다 정상적으로 로그인한 세션을 악용하는 방식으로 일어나기 때문에, 인증만 튼튼하고 권한과 테넌시가 느슨하면 실제로는 거의 무방비 상태와 다르지 않다.1
LLM 서비스에서는 이 구분이 더 중요하다. 예를 들어 고객지원용 AI SaaS를 만든다고 해보자.
사용자 A는 정상 로그인에 성공했다. 여기까지는 인증이다. 하지만 A가 자기 조직의 티켓만 볼 수 있는지, 다른 조직의 티켓도 검색할 수 있는지는 권한과 테넌시의 문제다. 더 나아가 "이번 요청은 요약만 허용되는지, 실제 티켓 상태를 변경하는 툴 호출까지 허용되는지"는 작업 단위 권한 문제다.
즉, LLM 서비스에서 보안 설계는 보통 아래 세 질문으로 요약된다.
이 요청은 누구의 요청인가?
이 요청은 어떤 행동까지 허용되는가?
이 요청은 어느 조직의 데이터 경계 안에서만 처리되어야 하는가?
이 셋 중 하나라도 흐려지면, 모델은 금세 엉뚱한 곳으로 달려간다.
왜 LLM 서비스에서는 권한 실수가 더 치명적인가
첫째, 모델은 대량의 데이터를 매우 빠르게 읽는다. 사람이 직접 화면을 넘기며 확인할 때는 한 번에 노출되는 정보량이 제한적이다. 하지만 LLM은 검색 결과 수십 개를 한 번에 컨텍스트로 넣고, 그 안의 민감한 내용을 몇 줄 요약으로 압축해버릴 수 있다.
둘째, 모델은 읽기만 하지 않는다. 툴 호출과 에이전트 구조를 붙이면 외부 시스템을 건드린다. CRM 조회, 티켓 수정, 결제 상태 확인, 내부 위키 검색, 코드 저장소 접근까지 모두 가능해진다. 권한 범위가 넓은 토큰을 아무 생각 없이 붙이면, 프롬프트 인젝션이나 모델 오판이 곧바로 실제 시스템 조작으로 이어질 수 있다.4
셋째, 데이터가 흐르는 표면이 많다. 사용자의 질문만 조심한다고 끝나지 않는다. 업로드 파일, 벡터 인덱스, 대화 이력, 응답 ID, 캐시, 로그, 트레이스, 백그라운드 작업, 툴 실행 결과까지 모두 권한 경계 안에 있어야 한다. 어느 한 군데라도 tenant_id나 access scope가 빠지면 거기서 새어 나온다.
결국 LLM 서비스의 보안은 "모델을 얼마나 똑똑하게 만들었는가"보다 "모델이 틀려도 어디까지밖에 못 하게 막았는가"로 평가해야 한다.
권한의 기본 원칙: 사용자, 서비스, 작업의 교집합
LLM 애플리케이션 권한 설계에서 가장 유용한 사고법은 대리 실행이다. 사용자는 직접 데이터를 찾지 않고, AI에게 "내 대신 찾아줘", "내 대신 정리해줘", "내 대신 실행해줘"라고 부탁한다. 이때 AI는 독립적인 주체처럼 보여도 실제로는 사용자를 대신해 행동하는 것이다.
따라서 LLM의 실제 권한은 단순히 "사용자 권한"이 아니고, "AI 서비스 권한"도 아니다. 사용자 권한, AI 서비스에 부여된 권한, 그리고 이번 작업에 필요한 권한의 교집합으로 봐야 한다.2
이 원칙은 실무에서 아주 강력하다.
예를 들어 사용자에게 문서 읽기 권한은 있지만 삭제 권한은 없다고 하자. 그리고 AI 서비스 계정도 문서 읽기 전용으로만 구성했다면, 모델이 무슨 말을 하든 삭제는 못 한다. 반대로 사용자에게는 관리자 권한이 있어도, 이번 작업이 "문서 요약"이라면 굳이 사용자 관리 API까지 열어둘 이유가 없다. 작업이 요구하는 최소 권한만 허용하면 된다.
이 교집합 원칙을 지키면 프롬프트가 조금 흔들려도 피해 범위가 줄어든다. 모델의 판단력에 보안을 맡기지 않고, 애초에 좁은 상자 안에서만 움직이게 만드는 셈이다.
인증 설계는 세 겹으로 나눠라
LLM 서비스를 설계할 때 인증은 하나가 아니다. 보통 세 층으로 나뉜다.
첫 번째는 사용자 인증이다. 브라우저나 모바일 앱에서 로그인한 최종 사용자를 확인한다. 이 레이어에서는 보통 OIDC, OAuth 2.0, SSO, JWT 기반 액세스 토큰을 쓴다. 요즘 SaaS에서는 짧은 수명의 액세스 토큰과 안전한 리프레시 토큰 회전을 기본값으로 보는 편이 맞다.1
두 번째는 서비스 간 인증이다. 프런트엔드가 호출한 API Gateway, 백엔드, 워커, 검색 서비스, 파일 처리 서비스가 서로를 믿을 수 있어야 한다. 여기서는 내부 JWT, mTLS, 클라우드의 managed identity 같은 방식을 쓴다. 사람은 로그인을 하지만, 서비스는 토큰이나 인증서로 대화한다.
세 번째는 모델 제공자 인증이다. OpenAI, Claude, Gemini, Azure OpenAI 같은 외부 LLM API를 호출할 때 필요한 인증이다. 이때는 API 키를 쓸 수도 있고, 클라우드 환경이라면 managed identity 같은 더 안전한 방법을 쓸 수도 있다. 중요한 점은, 최종 사용자 인증과 모델 제공자 인증은 완전히 다른 문제라는 것이다. 사용자 JWT를 받았다고 해서 곧바로 외부 모델 API를 호출해도 되는 게 아니다. 보통은 API Gateway에서 먼저 사용자 토큰과 클레임을 검증하고, 그다음에 서버가 별도의 자격 증명으로 모델 제공자에게 요청을 보낸다. 이런 다층 검증은 방어 심층화의 기본 패턴이다.3
간단히 말해, 브라우저는 사용자 토큰만 알고 있어야 하고, 백엔드는 내부 서비스 토큰을 알고 있어야 하며, LLM 제공자 키는 프록시나 백엔드만 알아야 한다.
브라우저에는 사용자 토큰만, 공급자 키는 절대 보내지 마라
LLM 기능을 붙이다 보면 가장 흔한 실수가 나온다. "일단 빨리 붙이자"는 마음으로 프런트엔드에서 직접 LLM API를 호출하게 만들고, 거기에 공급자 API 키를 넣어버리는 것이다. 개발 초기에는 편해 보이지만, 프로덕션 관점에서는 거의 금지사항에 가깝다.
정적 API 키는 노출되기 쉽고, 한 번 새면 회수 전까지 계속 재사용된다. 특히 LLM 애플리케이션에서는 시스템 프롬프트나 설정 값 안에 자격 증명을 숨겨두는 식의 편법이 매우 위험하다. 프롬프트 인젝션이나 디버그 출력, 로그 누락 한 번이면 키가 드러날 수 있다.4
안전한 구조는 반대다.
브라우저는 애플리케이션 서버에만 요청한다. 서버는 사용자 JWT를 검증하고, tenant_id와 권한을 확인한 뒤, 내부 프록시나 API Gateway를 통해 외부 모델을 호출한다. 이때 공급자 키는 시크릿 매니저나 키 볼트에 저장하고, 런타임에만 주입한다. 가능하면 API 키보다 만료 시간이 짧고 범위가 제한된 토큰이나 관리형 ID를 우선한다.34
이 구조의 장점은 명확하다. 키가 브라우저에 노출되지 않고, 프롬프트에도 들어가지 않으며, 어떤 사용자가 어느 모델을 얼마나 썼는지도 중앙에서 통제할 수 있다.
RBAC로 시작하되, ABAC로 마감하라
권한 모델을 설계할 때는 보통 RBAC부터 시작한다. 역할 기반 접근 제어다. 예를 들면 owner, admin, member, viewer 같은 역할이 있다. 이 방식은 이해하기 쉽고 운영도 편하다.
하지만 LLM 서비스는 역할만으로 부족한 경우가 많다. 같은 member여도 어떤 사용자는 자기 팀 문서만 볼 수 있고, 어떤 사용자는 특정 프로젝트만 볼 수 있다. 어떤 문서는 일반 등급이고, 어떤 문서는 인사 정보처럼 민감하다. 또 어떤 기능은 사내 네트워크에서만 허용하고, 어떤 요청은 근무 시간 안에서만 허용하고 싶을 수 있다.
이때 필요한 것이 ABAC, 즉 속성 기반 접근 제어다. 사용자 역할 외에도 tenant_id, project_id, document_classification, device_trust, request_time, subscription_plan 같은 속성을 함께 본다.14
실무에서는 대개 이렇게 가면 좋다.
기본 뼈대는 RBAC로 만든다. 누가 관리자이고 누가 일반 사용자인지 명확히 나눈다. 그 위에 민감한 데이터, 고위험 툴 호출, 결제나 설정 변경 같은 기능에는 ABAC 규칙을 덧댄다. 예를 들어 "해당 문서는 같은 tenant 소속이면서 HR 관리자 역할을 가진 사용자만 조회 가능"처럼 말이다.
LLM 서비스에서 특히 중요한 것은 읽기 권한과 실행 권한을 분리하는 것이다. 조회와 요약은 허용하되, 수정과 삭제는 별도 승인으로 분리하는 게 좋다. 모델은 글을 잘 쓰지만, 책임을 지지는 않기 때문이다.
테넌트 컨텍스트는 요청이 아니라 시스템 전체를 따라다녀야 한다
멀티테넌트 SaaS에서 정말 위험한 문장은 이것이다.
"tenant_id는 요청 바디에 있잖아요."
이 말은 거의 항상 문제를 부른다. 테넌트 컨텍스트는 클라이언트가 보낸 문자열 하나로 믿으면 안 된다. 인증 토큰에서 꺼내고, 서버가 검증하고, 이후의 모든 내부 객체와 저장소에 함께 흘려보내야 한다.7
LLM 서비스에서 tenant context가 붙어야 하는 대상은 생각보다 많다.
사용자 세션과 대화방
업로드 파일과 파싱 결과
청크, 임베딩, 벡터 인덱스
프롬프트 캐시와 응답 캐시
백그라운드 작업 ID와 상태 저장소
툴 호출 로그와 감사 로그
비용 집계, 레이트 리밋, 사용량 리포트
특히 상태를 저장하는 API를 쓸 때 더 조심해야 한다. 예를 들어 대화 상태를 response ID 같은 형태로 연결하는 API를 쓰면, 애플리케이션이 그 ID를 tenant 범위 안에서 저장하고 검증해야 한다. 같은 response ID라도 다른 tenant의 사용자가 참조할 수 있으면 바로 경계가 무너진다.6
RAG도 마찬가지다. 공유 인덱스를 쓴다면 모든 검색 쿼리에 tenant filter가 강제되어야 한다. 더 안전하게 가려면 tenant별 인덱스를 분리할 수도 있다. 어떤 방식을 쓰든 임베딩은 그냥 숫자 벡터가 아니라 민감한 데이터의 다른 표현이라는 사실을 잊으면 안 된다.67
LLM이 붙으면 데이터 분리 전략도 달라진다
멀티테넌트 설계에서 늘 나오는 질문이 있다. "데이터베이스를 나눌까요, 그냥 tenant_id 컬럼으로 나눌까요?" LLM 서비스에서는 이 질문을 조금 더 넓게 봐야 한다. 데이터베이스만이 아니라 모델 배포, 벡터 저장소, 파일 저장소, 캐시, 큐, 로그, 심지어 툴 실행 환경까지 분리 범위를 정해야 한다.
가장 단순한 방식은 공유 데이터베이스와 공유 테이블에 tenant_id를 두고 행 단위로 분리하는 것이다. 비용 효율이 좋고 운영이 쉽다. 대신 애플리케이션 코드와 쿼리, 백그라운드 작업, 로그 시스템까지 모두 tenant-aware해야 한다. 한 군데라도 필터가 빠지면 사고가 난다.
그보다 강한 방법은 테넌트별 스키마나 데이터베이스를 따로 두는 것이다. 운영 부담은 늘지만 사고 반경은 줄어든다. 더 높은 격리가 필요하면 특정 고객에게는 전용 모델 배포나 전용 리소스를 제공할 수도 있다. Azure OpenAI 기준으로도 공유 인스턴스, tenant별 전용 배포, tenant별 전용 리소스는 각각 격리 수준과 운영 복잡도가 다르다.5
여기서 중요한 실무 포인트가 하나 있다. 모델 제공자가 tenant별 접근제어를 대신 해주지 않는 경우가 많다. 예를 들어 공유된 모델 리소스 안에 여러 배포가 있더라도, 어떤 tenant가 어느 배포를 써야 하는지는 애플리케이션이 직접 강제해야 한다.5
즉, "클라우드 서비스가 알아서 막아주겠지"라고 생각하면 안 된다. 테넌트 경계는 보통 우리 코드가 지킨다.
툴 호출과 에이전트는 '과한 권한'을 가장 빨리 드러낸다
에이전트형 기능은 멋지다. 사용자가 "지난달 VIP 고객 불만 티켓을 찾아서 요약하고, 심각한 건 우선순위를 올려줘"라고 말하면 AI가 여러 시스템을 오가며 일을 끝내준다. 하지만 보안 입장에서는 이 기능이 가장 무섭다.
문제는 모델이 똑똑해서가 아니라, 우리가 보통 너무 넓은 권한을 준다는 데 있다. 하나의 범용 관리자 토큰으로 CRM도 읽고, 결제도 보고, 티켓도 수정하게 해두면, 모델이 잘못 행동했을 때 피해 범위가 너무 커진다.4
툴 호출이 들어가는 순간에는 몇 가지 원칙이 필요하다.
첫째, 서비스 계정과 사용자 권한을 섞지 않는다. "AI 봇" 전용 서비스 ID를 만들고, 필요한 도구에만 최소 범위 권한을 부여한다.4
둘째, 읽기와 쓰기를 다르게 취급한다. 조회와 요약은 자동화할 수 있어도, 수정·삭제·전송은 사용자 확인이나 워크플로 승인 단계를 거치게 한다.
셋째, 짧은 수명의 범위 제한 토큰을 쓴다. 한 번 탈취돼도 오래 못 쓰고, 할 수 있는 일도 적어야 한다.4
넷째, 비밀은 프록시가 주입한다. 에이전트 런타임이나 모델 프롬프트 안에 자격 증명을 직접 넣지 말고, 외부 프록시가 허용된 목적지에만 요청을 보내면서 런타임에만 토큰을 붙이게 만든다.4
이 구조를 잡아두면, 에이전트가 조금 엉뚱한 판단을 해도 "할 수 없는 일"이 많아진다. 보안은 늘 이쪽이 이긴다.
LLM API 키도 '팀 단위'로 관리하면 운영이 쉬워진다
멀티테넌트 SaaS를 운영하다 보면 "우리 서비스의 사용자 인증"과 "우리 서비스가 외부 LLM을 호출할 때 쓰는 키" 사이에 한 겹이 더 필요할 때가 많다. 예를 들어 팀별 예산, 모델 접근 권한, 사용량 집계를 따로 관리하고 싶을 수 있다.
이럴 때 쓸 수 있는 패턴이 가상 키, 팀 키, 워크스페이스 키 같은 중간 계층이다. 팀마다 별도 키를 발급하고, 이 키로 요청을 식별해 예산과 접근 권한을 적용하는 방식이다. 이런 키는 브라우저가 아니라 반드시 서버 측에서만 사용해야 하고, 키마다 허용 모델과 크레딧 한도를 분리하면 운영 통제가 쉬워진다.8
이 패턴이 좋은 이유는 두 가지다. 하나는 외부 LLM 공급자 키를 직접 노출하지 않아도 된다는 점이고, 다른 하나는 "어느 고객이 비용을 얼마나 썼는지"를 명확히 집계할 수 있다는 점이다. 멀티테넌트 AI 서비스에서 비용과 권한은 생각보다 자주 같은 테이블에 앉는다.
프록시와 API Gateway는 보안팀이 아니라 제품팀의 친구다
초기 개발자는 프록시나 API Gateway를 보면 귀찮아한다. 한 레이어 더 늘어나고, 요청도 한 번 더 지나가고, 설정할 것도 많아 보인다. 하지만 프로덕션의 관점에서는 거의 만능 공구에 가깝다.
Gateway나 프록시는 다음 일을 한곳에서 맡을 수 있다.
사용자 토큰과 JWT 클레임 검증
tenant_id와 scope 검사
공급자 인증 주입
모델별 접근 제어와 레이트 리밋
요청 크기, 토큰 수, 비용 상한 검사
감사 로그와 보안 이벤트 기록
특히 LLM 요청은 일반 API보다 비싸고, 느리고, 위험하다. 그러니 들어오는 요청을 애플리케이션 코드 곳곳에서 제각각 막는 것보다, 중앙 경계에서 최대한 표준화하는 편이 좋다. Azure API Management 예시에서도 사용자 측 OAuth 토큰 검증과, 백엔드 LLM 서비스에 대한 API 키 혹은 managed identity 인증을 별도로 조합하는 방식을 권장한다.3
프록시는 단순한 중계기가 아니라, "이 요청을 정말 보내도 되는가?"를 마지막으로 묻는 문지기다.
실전 예시: 조직용 문서 어시스턴트의 권한 흐름
조직별 문서 검색과 요약을 제공하는 SaaS를 예로 들어보자. 회사마다 문서가 다르고, 같은 회사 안에서도 부서별 접근 권한이 다르다.
사용자가 로그인하면 액세스 토큰에는 최소한 이런 정보가 들어 있어야 한다.
{
"sub": "user_123",
"tenant_id": "acme",
"roles": ["member"],
"scopes": ["docs:read", "chat:use"]
}이제 사용자가 "지난 분기 보안 감사 문서를 요약해줘"라고 요청한다.
API Gateway는 토큰의 서명, 만료 시간, issuer, audience를 검증한다.
백엔드는 토큰에서 tenant_id를 읽고, 요청 바디에 들어 있는 tenant_id 값은 무시한다.
문서 검색기는
tenant_id = 'acme'조건을 강제하고, 추가로 사용자 역할과 문서 ACL을 반영한다.검색 결과 청크만 모델 컨텍스트에 넣는다.
응답 ID를 저장할 때는
tenant_id + response_id형태로 묶어서 저장한다.사용량 로그에는 tenant_id, user_id 해시, 모델명, 토큰 사용량, 지연 시간만 남기고 원문 프롬프트는 필요할 때만 별도 보호 저장소에 남긴다.
여기서 중요한 점은, 어느 단계도 "사용자가 아까 로그인했으니 괜찮겠지"로 넘어가지 않는다는 것이다. 검색할 때도, 저장할 때도, 재호출할 때도 tenant와 권한을 다시 묻는다.
이 구조가 귀찮아 보여도, 실제 사고는 대부분 "그 한 번쯤은 괜찮겠지"에서 나온다.
로그, 캐시, 백그라운드 작업도 테넌시 안에 넣어라
개발팀이 가장 자주 놓치는 곳은 메인 요청 경로가 아니라 주변부다.
예를 들어 문서 요약을 비동기 작업으로 돌린다고 해보자. 큐에 들어가는 작업 payload에 tenant_id가 없다면, 워커는 누구 문서인지 모른 채 결과를 저장하게 된다. 캐시 키에 tenant_id가 빠지면 A 회사 사용자가 B 회사 사용자의 요약 결과를 그대로 받는 황당한 일이 생길 수 있다. 로그 뷰어에서 support 엔지니어가 tenant 필터 없이 검색할 수 있으면 그것도 이미 사고다.7
그래서 다음 객체는 항상 tenant 범위 안에 있어야 한다.
job_id, task_id, response_id 같은 상태 식별자
캐시 키와 세션 키
파일 저장 경로와 버킷 prefix
로그 인덱스와 분석 대시보드
비용 집계 레코드와 알림 규칙
좋은 멀티테넌시 설계는 보통 화려하지 않다. 대신 모든 키 이름과 모든 테이블 컬럼이 매우 성실하다.
엔터프라이즈 환경에서는 네트워크 경계도 고려하라
대형 고객을 상대하면 사용자 로그인만으로는 부족할 수 있다. 회사는 종종 "우리 직원이 회사 계정으로만 AI 도구를 쓰게 하고 싶다"고 요구한다. 이때는 네트워크 프록시 수준에서 특정 조직 계정만 허용하는 통제가 필요할 수 있다. Anthropic의 Claude Enterprise처럼 프록시가 헤더를 주입해 승인된 조직 계정만 접근하게 하는 tenant restrictions 같은 기능은 이런 요구를 반영한 사례다.9
이런 기능은 모든 SaaS에 반드시 필요한 것은 아니다. 하지만 금융, 의료, 공공, 대기업 환경으로 갈수록 "누가 로그인했는가"보다 "어느 네트워크, 어느 조직 정책 아래서 접속했는가"가 중요해진다. 즉, 엔터프라이즈용 LLM 서비스는 애플리케이션 계층 인증을 넘어 네트워크 계층 제어까지 검토해야 한다.
자주 터지는 안티패턴
실무에서 반복해서 보게 되는 안 좋은 설계를 몇 가지 정리해보자.
첫 번째는 프런트엔드가 보낸 tenant_id를 그대로 믿는 것이다. 이건 멀티테넌시가 아니라 자기신고제다.
두 번째는 모든 툴에 하나의 관리자 토큰을 공유하는 것이다. 편해 보이지만, 나중에는 "왜 AI가 결제 취소도 되고 고객 내보내기도 되죠?"라는 질문을 받게 된다.
세 번째는 로그인 시점에만 권한을 확인하는 것이다. LLM 서비스는 검색, 툴 호출, 파일 처리, 결과 조회가 모두 별도 경로이므로, 매 단계에서 권한을 다시 확인해야 한다.
네 번째는 공유 벡터 인덱스를 쓰면서 tenant filter를 선택사항으로 두는 것이다. 선택사항은 언젠가 빠진다.
다섯 번째는 프롬프트, 파일, 응답을 무기한 보관하는 것이다. 저장된 데이터는 언젠가 검색되고, 검색되는 데이터는 언젠가 새어나간다.
여섯 번째는 운영자 도구를 예외 구역으로 만드는 것이다. 고객 지원용 관리자 화면이 모든 tenant 데이터를 한 번에 볼 수 있다면, 그 화면이 곧 가장 위험한 시스템이다.
설계 체크리스트: 이 장의 내용을 제품에 옮기기
챕터를 마무리하기 전에, 실제 구현에 바로 옮길 수 있도록 기준선을 정리해보자.
인증 측면에서는 사용자 인증, 서비스 인증, 공급자 인증을 분리하자. 브라우저에는 사용자 토큰만 두고, 공급자 키는 서버 또는 프록시에서만 주입하자. 정적 키보다 짧은 수명의 토큰과 관리형 ID를 우선하자.34
권한 측면에서는 RBAC를 기본으로 깔고, 민감한 데이터와 실행형 기능에는 ABAC 규칙을 덧대자. 특히 LLM의 유효 권한은 사용자, 서비스, 작업의 교집합이라는 원칙을 지키자.2
테넌시 측면에서는 tenant_id를 토큰에서 검증해 꺼내고, 세션, 파일, 인덱스, 캐시, 작업, 로그까지 전 구간에 전파하자. 상태 저장형 LLM API의 response ID나 background task ID도 tenant 범위 안에서만 재사용되게 하자.67
운영 측면에서는 팀별 키, 예산, 모델 접근 권한, 레이트 리밋, 사용량 집계를 분리하자. 멀티테넌트 AI 서비스는 결국 비용 통제와 보안 통제가 같은 방향을 가리킨다.58
마무리: AI 시대의 권한 설계는 더 보수적이어야 한다
좋은 LLM 서비스는 답을 잘하는 서비스가 아니다. 잘못된 요청에 대해서도 절대 넘지 말아야 할 선을 넘지 않는 서비스다.
인증은 문을 여는 일이다. 권한은 방마다 자물쇠를 다는 일이다. 테넌시는 아예 다른 회사 건물로 잘못 들어가지 못하게 울타리를 치는 일이다. LLM 서비스에서는 이 셋이 모두 필요하다.
모델은 똑똑해질수록 더 많은 일을 대신해준다. 그래서 우리는 모델을 더 믿는 대신, 모델이 움직일 수 있는 범위를 더 작고 선명하게 만들어야 한다. 그게 실전 서비스에서의 안전한 AI 설계다.
참고
1How to Secure API Authentication and Authorization in SaaS Apps
2Chapter VIII: Authorization in LLM Applications
3Authenticate and authorize access to LLM APIs by using Azure API Management
4How to expose APIs to LLMs without breaking security
5Multitenancy and Azure OpenAI
6Multitenancy and Azure OpenAI
7Best Practices for Deploying ChatGPT in a Multi-Tenant Architecture
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.