
AI 에이전트가 OAuth 토큰을 직접 보유해서는 안 되는 이유
요약
AI 에이전트가 OAuth 토큰과 같은 민감한 자격 증명을 직접 보유할 때 발생하는 보안 위험을 분석합니다. 에이전트의 의사결정과 실제 실행을 분리하여 보안을 강화하는 커넥터 게이트웨이 아키텍처와 OpenConnector 프로젝트를 소개합니다.
핵심 포인트
- 에이전트의 도구 선택과 자격 증명 사용은 분리되어야 함
- OAuth 토큰 직접 보유는 취약한 아키텍처 경계를 형성함
- 커넥터 게이트웨이를 통한 인증 및 실행 분리 권장
- 액션 계약(Action contracts)을 통한 명시적 작업 수행 필요
채팅만 할 수 있는 에이전트는 통제하기 쉽습니다. Gmail을 읽고, GitHub 이슈를 열고, Notion을 업데이트하며, Slack 메시지를 보낼 수 있는 에이전트는 훨씬 더 유용하지만, 보안을 유지하기는 훨씬 더 어렵습니다.
불편한 부분은 도구 호출 (Tool call) 그 자체가 아닙니다. 그 주변의 모든 것입니다:
- OAuth 리프레시 토큰 (Refresh token)은 어디에 저장되는가?
- 에이전트가 이를 볼 수 있는가?
- 이 특정 에이전트가 실행할 수 있는 작업은 무엇인가?
- "메시지 전송"은 어떤 계정을 의미하는가?
- 실행 실패 후 어떤 일이 일어났는지 알 수 있는가?
- 동일한 제품의 통합 (Integration)이 2개에서 50개로 늘어나면 무엇이 변하는가?
작은 내부 스크립트의 경우, API 키를 환경 변수 (Environment variable)에 넣는 것으로 충분할 수 있습니다. 하지만 에이전트를 많은 사용자 계정에 연결하는 제품의 경우, 이는 취약한 아키텍처 경계 (Architectural boundary)가 됩니다.
이 글에서는 다른 접근 방식을 살펴봅니다. 인증 (Authentication)과 제공자별 실행 (Provider-specific execution)을 커넥터 게이트웨이 (Connector gateway) 뒤에 배치한 다음, 에이전트가 명시적인 액션 계약 (Action contracts)을 통해 작업하도록 하는 것입니다. 구체적인 예시로 Apache-2.0 오픈 소스 프로젝트인 OpenConnector를 사용하겠습니다. 이 글을 쓰는 시점에 해당 GitHub 저장소는 4,000개 이상의 스타 (Stars)를 기록했습니다. 이는 인기가 코드, 보안 모델 및 제공자 커버리지를 직접 평가하는 것을 대신할 수는 없지만, 이 문제가 개발자들에게 공감을 불러일으키고 있다는 유용한 신호입니다.
도구 선택과 자격 증명 사용은 서로 다른 작업입니다
에이전트가 현재 GitHub 사용자를 가져와야 한다고 가정해 봅시다. 에이전트는 액션 (Action)이 존재한다는 것, 어떤 입력을 받는지, 그리고 어떤 출력을 반환하는지를 알아야 합니다. 요청을 승인하는 데 사용되는 개인 액세스 토큰 (Personal access token)을 알 필요는 없습니다.
이것들은 두 가지 별개의 책임입니다:
Agent
무엇을 할지 결정함
액션 (Action)을 선택함
...
이 경계(boundary)가 모든 에이전트 보안 문제를 해결해 주는 것은 아닙니다. 프롬프트 인젝션 (Prompt injection)은 여전히 잘못된 도구 선택을 유발할 수 있습니다. 과도하게 넓은 OAuth 스코프 (scope)는 여전히 과도하게 넓습니다. 게이트웨이 (gateway)가 침해되는 것 또한 여전히 심각한 문제입니다.
이 경계가 제공하는 것은 자격 증명 (credentials)을 저장하고 순환 (rotate)하며, 액션 (Actions)을 제한하고, 계정을 분리하며, 실행을 검사할 수 있는 단일 지점이라는 것입니다. 이는 모든 에이전트 호스트와 도구 구현부를 통해 제공자 비밀값 (provider secrets)을 전달하는 것보다 추론하기가 더 쉽습니다.
OpenConnector가 경계 뒤에 배치하는 것들
OpenConnector는 그렇지 않으면 독립적으로 늘어나는 경향이 있는 여러 구성 요소를 결합합니다:
- API 키, OAuth 2.0, 커스텀 인증 (custom auth), 그리고 인증이 필요 없는 (no-auth) 제공자를 위한 자격 증명 (credentials);
- 요청 및 응답 스키마 (schema)를 포함한 제공자 액션 (provider Actions) 카탈로그;
- 동일한 제공자의 여러 계정을 위한 명명된 연결 (named connections);
- 액션 허용/차단 정책 (Action allow/block policies) 및 별도의 제공자-프록시 정책 (provider-proxy policies);
- 런타임 토큰 (runtime tokens), 선택적 JWT 인증, 그리고 관리자 경계 (admin boundary);
- 검사를 위한 비식별화된 실행 로그 (redacted run logs) 및 웹 콘솔 (Web Console);
- 동일한 액션 모델에 대한 MCP, HTTP, OpenAPI, SDK 및 CLI 액세스.
이 프로젝트는 현재 1,000개 이상의 제공자와 10,000개 이상의 사전 구축된 액션 (prebuilt Actions)을 홍보하고 있습니다. 이 수치들은 카탈로그의 규모로 간주해야 하며, 모든 제공자가 귀하에게 필요한 모든 작업을 갖추고 있다는 보장으로 받아들여서는 안 됩니다. 통합 플랫폼을 채택하기 전에, 귀하의 제품에 필요한 정확한 액션 (Actions), 스코프 (scopes), 그리고 예외 케이스 (edge cases)를 확인하십시오.
로컬에서 하나의 액션 실행하기
가장 간단한 테스트는 계정이나 자격 증명이 필요하지 않습니다. 저장소 (repository)를 클론(clone)한 다음, 게시된 컨테이너를 시작하십시오:
docker compose up
Compose 파일은 ghcr.io/oomol-lab/open-connector:latest를 가져옵니다. 런타임 (runtime)이 준비되면, 로컬 콘솔은 http://localhost:3000에서 사용할 수 있으며, 생성된 API 레퍼런스 (API reference)는 http://localhost:3000/docs에서 확인할 수 있습니다.
Hacker News는 편리한 인증이 필요 없는 (no-auth) 액션을 제공합니다:
curl -s -X POST \
http://localhost:3000/v1/actions/hackernews.get_top_stories \
-H 'content-type: application/json' \
...
무엇인가를 실행하기 전에, 클라이언트는 카탈로그(catalog)를 탐색할 수 있습니다:
curl -s http://localhost:3000/v1/actions
curl -s "http://localhost:3000/v1/actions?service=hackernews"
또한 하나의 액션(Action)에 대해 에이전트가 읽을 수 있는 압축된 가이드를 요청할 수도 있습니다:
curl -s \
http://localhost:3000/api/actions/hackernews.get_top_stories/agent.md
이러한 '탐색-검사-실행(discover-inspect-execute)' 패턴은 매우 중요합니다. 수천 개의 도구 정의(tool definitions)를 에이전트의 컨텍스트(context)에 로드하는 것은 낭비이며 도구 선택 능력을 저하시킬 수 있습니다. 작은 탐색 영역(discovery surface)을 제공하면 에이전트가 먼저 검색하고, 관련 계약(contract)만을 검사하며, 입력을 이해한 후에 실행할 수 있게 합니다.
로컬 MCP 엔드포인트(endpoint)는 해당 모델을 따릅니다. 카탈로그의 모든 액션에 대해 하나의 MCP 도구를 노출하는 대신, 앱 및 연결(connections) 목록 표시, 액션 검색, 액션 가이드 검색, 그리고 액션 실행을 위한 소수의 도구 세트를 제공합니다.
에이전트에게 전달하지 않고 자격 증명(credential) 추가하기
GitHub는 개인 액세스 토큰(personal access token)을 사용할 수 있기 때문에 인증이 필요한 간단한 예시가 됩니다. 로컬 개발 환경에서는 관리 API(administration API)를 통해 연결을 저장하십시오:
curl -s -X PUT http://localhost:3000/api/connections/github \
-H 'content-type: application/json' \
-d '{
...
이제 요청에 해당 자격 증명을 포함하지 않고 액션을 실행합니다:
curl -s -X POST \
http://localhost:3000/v1/actions/github.get_current_user \
-H 'content-type: application/json' \
...
여러 개의 GitHub 계정이 있는 경우, personal 및 work와 같이 이름이 지정된 연결(named connections)을 생성하십시오. 호출자는 의도한 연결을 명시적으로 선택합니다. 지정된 연결이 누락되면 다른 계정으로 조용히 전환(fallback)되는 대신 에러를 반환합니다. 이는 예상치 못한 위험한 실수를 방지하는 작은 동작입니다.
OAuth 제공자(providers)를 직접 호스팅(self-hosting)할 때는 더 많은 설정이 필요합니다. 자체 OAuth 앱을 등록하고, 콜백 URL(callback URL)을 구성하며, 클라이언트 비밀(client secret)을 보호하고, 토큰 수명 주기(token lifecycle)를 운영해야 합니다. OpenConnector는 이러한 작업을 중앙 집중화하지만, 그렇다고 해서 이러한 책임이 사라지는 것은 아닙니다.
모든 제공자에 대해 OAuth 애플리케이션을 직접 등록하고 유지 관리하고 싶지 않다면, OOMOL 호스팅 커넥터(OOMOL-hosted connectors)가 동일한 모델의 SaaS 버전을 제공합니다. OOMOL이 OAuth 애플리케이션을 공급하고 커넥터 인프라를 실행하므로, 사용자가 직접 제공자의 클라이언트 자격 증명(client credentials)을 신청하지 않고도 지원되는 계정을 승인(authorize)할 수 있도록 할 수 있습니다. 사용자는 여전히 제공자의 일반적인 동의 흐름(consent flow)을 완료합니다. "관리형 OAuth (managed OAuth)"는 애플리케이션 자격 증명과 런타임(runtime)이 사용자를 대신해 운영된다는 의미이지, 승인 과정이 우회된다는 의미가 아닙니다.
따라서 호스팅 방식은 제품을 검증하거나 통합(integration) 기능을 빠르게 출시할 때 유용합니다. 트레이드오프(tradeoff)는 실행이 제3자 서비스와 해당 서비스의 가격, 가용성 및 데이터 처리 약관에 의존한다는 점입니다. 호스팅 런타임과 오픈 소스 런타임은 동일한 제공자 ID(provider IDs), 액션 ID(Action IDs) 및 계약(contracts)을 사용하기 때문에, 팀은 관리형 커넥터로 시작하면서도 나중에 프라이빗 배포(private deployment)로 나아갈 수 있는 경로를 유지할 수 있습니다.
프로덕션 투입 전 확인해야 할 제어 항목들
성공적인 API 응답을 받는 것은 첫 번째 테스트일 뿐입니다. 실제 계정을 연결하기 전에, 저는 다음과 같은 제어 항목들을 확인할 것입니다.
1. 관리(administration)와 실행(execution)을 분리할 것
웹 콘솔(Web Console), /api/*, 그리고 문서 엔드포인트(documentation endpoints)는 관리자 토큰(admin token)으로 보호할 수 있습니다. 에이전트 대상인 /v1/* 및 /mcp 호출은 런타임 토큰(runtime tokens) 또는 구성된 JWT 액세스 토큰(access tokens)을 사용합니다. 관리 인터페이스를 에이전트 런타임과 동일한 신뢰 수준으로 노출하지 마십시오.
2. 각 런타임에 최소한의 유용한 액션(Action) 세트만 부여할 것
OpenConnector는 허용(allowed) 및 차단(blocked)된 액션(Action) 패턴을 지원합니다. 연구용 에이전트(research agent)는 읽기 작업(read operations)은 필요할 수 있지만, 메시지 전송이나 기록 삭제 액션(Actions)은 필요하지 않을 수 있습니다. 시스템 프롬프트(system prompt)에만 의존하는 대신, 이러한 차이점을 정책(policy)으로 인코딩하십시오.
프로바이더 프록시(Provider proxy) 액세스는 액션(Action) 액세스와 별도로 제어됩니다. 이러한 구분은 유용합니다. 큐레이션된 GitHub 액션(GitHub Action)을 실행할 권한이 있다고 해서, GitHub API에 임의의 요청을 보낼 권한까지 자동으로 부여되어서는 안 됩니다.
3. 저장 데이터(data at rest) 및 백업 보호
로컬 런타임(local runtime)은 기본적으로 SQLite에 상태(state)를 저장합니다. OOMOL_CONNECT_ENCRYPTION_KEY를 사용하면 저장된 자격 증명(credentials), OAuth 클라이언트 설정, 그리고 완료된 멱등성 응답(idempotent responses)에 대한 암호화(encryption)를 활성화할 수 있습니다.
암호화는 운영의 일부일 뿐입니다. 배포 시에는 여전히 키 관리(key management), 파일 권한(file permissions), 백업(backups), 호스트 액세스(host access), 로테이션(rotation) 및 사고 대응(incident response)에 대한 계획이 필요합니다.
4. 로그를 보안 민감 정보로 취급할 것
민감 정보가 삭제된(Redacted) 실행 기록은 원본 자격 증명(raw credentials)을 의도적으로 저장하지 않으면서도 "에이전트가 무엇을 했는가?"라는 질문에 답하는 데 유용합니다. 하지만 이러한 기록에는 여전히 프로바이더 이름, 액션 ID(Action IDs), 계정 라벨(account labels), 타이밍, 오류 및 비즈니스 컨텍스트(business context)가 포함될 수 있습니다. 누가 이를 읽을 수 있는지, 그리고 얼마나 오래 보관해야 하는지를 결정하십시오.
5. 부수 효과(side effects)를 고려하여 재시도(retries) 설계
HTTP 액션(Action) 엔드포인트는 Idempotency-Key를 수락합니다. 동일한 키를 사용하여 동일한 액션(Action), 입력(input), 연결(connection)을 재시도하면, 작업을 다시 실행하는 대신 완료된 결과를 재현(replay)할 수 있습니다.
이는 레코드 생성이나 메시지 전송과 같은 작업에 도움이 되지만, 상위 프로바이더(upstream provider)에 의한 정확히 한 번 실행(exactly-once execution)을 보장하는 것은 아닙니다. MCP 액션(Action) 실행 또한 HTTP 멱등성 헤더(idempotency header)를 수락하지 않습니다. 워크플로(Workflow) 코드는 여전히 불확실한 결과(uncertain outcomes)를 신중하게 처리해야 합니다.
배포 모델 선택
OpenConnector는 Node.js 또는 Docker를 통해 로컬에서 실행하거나, Fly.io와 같은 Docker 인프라에서, 또는 D1, R2 및 정적 자산(Static Assets)을 사용하는 Cloudflare Workers에서 실행할 수 있습니다. 또한 OOMOL에서 호스팅하는 경로도 있습니다.
올바른 선택은 주로 책임(responsibility)에 관한 문제입니다:
| 모델 | 당신이 제어하는 것 | 당신이 운영하는 것 |
|---|---|---|
| 로컬 또는 셀프 호스팅 (Self-hosted) | 런타임 (Runtime), 데이터, 자격 증명 (Credentials), 네트워크 경계 (Network boundary) | OAuth 앱, 비밀값 (Secrets), 스토리지, 백업, 업그레이드 |
| ... |
자격 증명(Credential)의 위치, 네트워크 액세스 또는 컴플라이언스(Compliance)로 인해 프라이빗 인프라가 필요한 경우 셀프 호스팅(Self-hosting)은 가치가 있습니다. 하지만 운영 작업을 포함하면 자동으로 더 저렴해지는 것은 아닙니다. 관리형 서비스(Managed service)는 시작하기에 더 빠르지만, 외부 의존성(External dependency)이 추가됩니다. 보편적으로 정답인 방식은 없습니다.
커넥터 게이트웨이(Connector gateway)가 잘못된 도구인 경우
게이트웨이는 또 다른 서비스, 또 다른 장애 경계(Failure boundary), 그리고 업그레이드해야 할 또 다른 구성 요소를 추가합니다. 만약 당신의 애플리케이션이 하나의 제공자(Provider), 하나의 계정, 그리고 세 개의 안정적인 API 호출만을 가진다면, 제공자의 공식 SDK가 가장 단순하고 최선의 해결책일 수 있습니다.
게이트웨이 접근 방식은 다음 중 여러 사항이 해당될 때 그 가치를 발휘하기 시작합니다:
- 사용자가 자신의 계정을 직접 연결할 때
- 제품이 많은 제공자(Provider)를 지원할 때
- 에이전트(Agent)가 하나 이상의 호스트 또는 프로토콜에서 실행될 때
- 여러 계정을 안전하게 선택해야 할 때
- 에이전트나 환경에 따라 액세스 정책(Access policies)이 다를 때
- 자격 증명(Credentials)이 에이전트 프로세스 외부에 유지되어야 할 때
- 운영자(Operator)가 실패한 호출을 조사할 수 있는 공유된 장소가 필요할 때
아키텍처는 카탈로그의 크기가 아니라 문제를 따라야 합니다.
실무적인 평가 체크리스트
OpenConnector—또는 다른 커넥터 플랫폼—을 고려하고 있다면, 하나의 실제 워크플로우(Workflow)로 테스트하고 다음 질문에 답해 보세요:
- 필요한 정확한 제공자 작업(Provider operations)과 OAuth 범위(Scopes)를 지원하는가?
- 에이전트가 관련 없는 액션(Actions)에 접근하는 것을 방지할 수 있는가?
- 사용자가 여러 계정을 안전하게 구분하고 선택할 수 있는가?
- 자격 증명(Credentials), 암호화 키, 로그, 백업은 어디에 저장되는가?
- 제공자의 속도 제한(Rate limits) 및 부분적 실패(Partial failures)를 어떻게 처리할 것인가?
- 부수 효과(Side effects)가 있는 액션에 대한 재시도(Retry) 동작은 어떠한가?
- 모든 액션 계약(Action contract)을 변경하지 않고 관리형 환경과 셀프 호스팅 환경 사이를 이동할 수 있는가?
- 누가 커넥터 런타임(Connector runtime)을 업그레이드하고 모니터링할 것인가?
이러한 답변들은 통합(integrations) 페이지에 표시된 로고의 개수보다 더 중요합니다.
OpenConnector는 GitHub에서 확인할 수 있습니다. 계속 학습하기 가장 좋은 곳은 퀵 스타트 (quick start), 런타임 API 및 MCP 레퍼런스 (Runtime API and MCP reference), 그리고 자격 증명 문서 (credential documentation)입니다.
만약 여러분의 에이전트가 이미 외부 서비스를 사용하고 있다면, 현재 자격 증명 경계(credential boundary)를 어디에 두고 계신가요?
고지 사항: 이 기사는 AI의 도움을 받아 작성되었으며, 연결된 프로젝트 문서를 바탕으로 검토되었습니다. 최종적인 주장과 예시는 인간 저자에게 책임이 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기