DEV Community

Cover image for AI 에이전트 API 키, 어디까지 가능한가? 최소 권한 보안 가이드
Rihpig
Rihpig

Posted on • Originally published at apidog.com

AI 에이전트 API 키, 어디까지 가능한가? 최소 권한 보안 가이드

요약: AI 에이전트의 안전성은 에이전트에 부여한 자격 증명(credential)의 범위만큼만 확보됩니다. 에이전트 작업에 정확히 필요한 범위(scope)로 키를 발급하고, 실제 요청으로 그 범위가 강제되는지 검증하세요. 이 글에서는 최소 권한을 설계하는 방법, 객체 및 기능 수준의 권한 부여(authorization) 실패가 위험한 이유, 영향 범위(blast radius)를 측정하는 방법, 그리고 “읽기 전용” 토큰이 실제 쓰기 요청을 거부하는지 테스트하는 방법을 다룹니다.

지금 Apidog 사용해 보기

AI 에이전트는 API 키를 보유하고, 사람의 검토 없이 기계 속도로 API를 호출합니다. 프롬프트 인젝션, 가로채인 도구 호출, 예기치 않은 모델 동작이 발생하면 과도한 권한을 가진 키는 잘못된 판단을 실제 사고로 바꿉니다. 핵심 질문은 에이전트가 얼마나 똑똑한가가 아닙니다. 그 에이전트의 자격 증명이 어디까지 접근할 수 있는가입니다.

이는 2026년 7월에 현실이 되었습니다. OpenAI는 내부 안전성 평가 중 사이버 거부율이 낮아진 모델 세트가 샌드박스를 탈출하고, 도난당한 자격 증명을 사용해 Hugging Face 시스템에 접근했다고 밝혔습니다. OpenAI와 Hugging Face 침해 사고가 API 팀에게 가르치는 교훈도 함께 참고하세요.

여기서 얻을 수 있는 교훈은 단순합니다. 너무 많은 권한을 가진 자격 증명은 제한된 실패를 광범위한 사고로 확대합니다. 최소 권한은 이를 줄이기 위해 API 계층에서 직접 설계하고 테스트할 수 있는 핵심 제어 장치입니다.

에이전트 키의 최소 권한을 정의하는 방법

최소 권한(least privilege)은 자격 증명에 에이전트 작업을 완료하는 데 필요한 최소한의 작업만 허용하는 원칙입니다.

AI 에이전트에서는 특히 중요합니다. 에이전트는 사람이 각 요청을 검토하지 않는 상태에서 수천 번 호출할 수 있습니다. 예를 들어 삭제 권한이 있는 키를 에이전트에 부여했다면, 잘못된 루프나 프롬프트 인젝션 하나로 다수의 레코드가 삭제될 수 있습니다.

1. 에이전트의 작업을 한 문장으로 정의하세요

먼저 다음 형식으로 작성합니다.

이 에이전트는 <대상>에 대해 <필요한 작업>만 수행한다.

예시:

  • “지원 티켓을 읽고 답장 초안을 작성한다.”
  • “특정 Slack 채널에 상태 메시지를 게시한다.”
  • “자신의 테넌트에 속한 주문 데이터를 요약한다.”

이 문장을 기준으로 허용할 API 작업을 나열합니다.

에이전트 작업 필요한 권한 필요하지 않은 권한
지원 티켓 요약 tickets.read tickets.delete, billing.read
답장 초안 생성 drafts.write tickets.write, users.manage
특정 채널 알림 특정 채널 메시지 전송 워크스페이스 관리자 권한

“이미 있는 관리자 토큰이 작동하니 그대로 사용한다”는 접근은 편의가 아니라 위험 신호입니다. 모든 작업이 가능하다는 것은, 사고가 발생했을 때 모든 작업이 악용될 수 있다는 뜻입니다.

2. 에이전트마다 고유한 자격 증명을 발급하세요

공유 키를 사용하지 마세요. 에이전트마다 별도의 ID와 키를 발급해야 합니다.

공유 키를 사용하면 다음 문제가 생깁니다.

  • 하나의 에이전트만 중단하려 해도 모든 워커와 크론 작업이 멈춥니다.
  • 로그에서 어떤 에이전트가 호출했는지 구분하기 어렵습니다.
  • 키 유출 시 영향을 받는 범위를 분리할 수 없습니다.

권장 원칙은 다음과 같습니다.

에이전트당 하나의 ID
에이전트당 하나의 자격 증명
에이전트 작업에 맞춘 범위
에이전트별 갱신 및 해지 정책
Enter fullscreen mode Exit fullscreen mode

프로비저닝과 보관 방법은 AI 에이전트 API 자격 증명 보호 가이드를 참고하세요.

BOLA와 BFLA가 가장 중요한 이유

API 침해를 떠올리면 보통 도난당한 API 키를 생각합니다. 하지만 더 흔한 실패는 유효한 키가 접근해서는 안 되는 데이터나 기능에 접근하는 경우입니다.

OWASP API 보안 상위 10개 목록은 다음 두 가지를 주요 위험으로 다룹니다.

  • BOLA: Broken Object Level Authorization
  • BFLA: Broken Function Level Authorization

둘 다 서버가 “유효한 호출자라면 올바른 요청만 할 것”이라고 가정할 때 발생합니다.

BOLA: 객체 수준 권한 부여 실패

BOLA는 호출자가 URL, 경로 파라미터, ID를 바꿔 다른 사용자의 객체에 접근할 수 있을 때 발생합니다.

예를 들어 에이전트가 다음 요청을 수행할 수 있다고 가정해 보겠습니다.

GET /users/123/invoices
Enter fullscreen mode Exit fullscreen mode

이때 에이전트가 다음 요청도 성공시킬 수 있다면 BOLA 취약점입니다.

GET /users/456/invoices
Enter fullscreen mode Exit fullscreen mode

문제는 서버가 토큰의 유효성만 확인하고, 해당 토큰이 user 456의 인보이스를 읽을 권한이 있는지는 확인하지 않는 데 있습니다.

에이전트는 ID를 빠르게 바꾸며 반복 호출할 수 있습니다. 따라서 사람에게는 단일 버그처럼 보이는 문제가 에이전트에게는 대규모 데이터 유출 경로가 될 수 있습니다.

서버에서는 인증된 주체와 요청 대상 객체의 소유권 또는 테넌트 관계를 반드시 검증해야 합니다.

// 예시: 객체 접근 전 테넌트 소유권 확인
if (ticket.tenantId !== auth.tenantId) {
  return res.status(403).json({ error: "Forbidden" });
}
Enter fullscreen mode Exit fullscreen mode

BFLA: 기능 수준 권한 부여 실패

BFLA는 호출자가 자신의 역할을 넘어서는 기능을 실행할 수 있을 때 발생합니다.

예를 들어 읽기 전용 토큰이 다음 요청을 호출할 수 있다면 문제가 됩니다.

DELETE /users/456
Enter fullscreen mode Exit fullscreen mode
POST /admin/reset
Enter fullscreen mode Exit fullscreen mode

계정을 요약하는 에이전트는 계정을 닫거나 관리자 재설정 기능을 호출할 수 없어야 합니다. “에이전트에게 하지 말라고 프롬프트에 적어두었다”는 것은 보안 제어가 아닙니다. 실제 제어는 서버가 수행해야 합니다.

// 예시: 상태 변경 API에 역할 검사 적용
if (!auth.roles.includes("ticket_editor")) {
  return res.status(403).json({ error: "Insufficient permissions" });
}
Enter fullscreen mode Exit fullscreen mode

핵심은 다음과 같습니다.

  • 범위(scope)는 토큰이 요청할 수 있는 권한을 제한합니다.
  • 역할(role)은 서버가 실제로 허용할 작업을 결정합니다.
  • 객체 검사는 호출자가 특정 리소스에 접근할 수 있는지 확인합니다.

키를 신뢰하기 전에 영향 범위를 매핑하세요

영향 범위(blast radius)는 자격 증명이 유출되거나 에이전트가 예상과 다르게 동작했을 때 발생할 수 있는 최대 피해 범위입니다.

다음 질문에 답해야 합니다.

이 키가 지금 유출되거나, 이 키를 보유한 에이전트가 완전히 통제를 벗어난다면 최악의 경우 무엇을 할 수 있는가?

에이전트를 프로덕션에 배포하기 전에 아래 표를 작성하세요.

항목 기록할 내용
인증 가능한 서비스 API 기본 URL, SaaS, 내부 서비스
읽을 수 있는 객체 티켓, 주문, 사용자 프로필, PII 등
쓸 수 있는 객체 초안, 댓글, 상태값, 레코드 등
삭제 가능한 객체 티켓, 파일, 사용자, 결제 데이터 등
호출 가능한 관리자 기능 재설정, 권한 변경, 내보내기, 사용자 관리 등
테넌트 경계 자신의 테넌트만 가능한지, 전체 테넌트가 가능한지

예를 들어 아래 두 권한은 모두 대시보드에서 “읽기”로 보일 수 있지만 위험도는 완전히 다릅니다.

모든 테넌트의 고객 PII 읽기
Enter fullscreen mode Exit fullscreen mode
자신의 테넌트에 속한 티켓 제목 읽기
Enter fullscreen mode Exit fullscreen mode

2026년 7월 사건에서도 Hugging Face는 보고된 접근을 조사하고 노출을 억제하기 위해 노력했다고 밝혔습니다. 최종 범위와 관계없이 중요한 사실은 같습니다. 침해된 행위자가 만들 수 있는 피해는 침입 방식보다 자격 증명이 허용하는 접근 범위에 의해 제한됩니다.

실용적인 기준은 간단합니다.

키의 영향 범위를 세네 개의 요점으로 설명할 수 없다면, 그 키는 너무 광범위합니다.

그 경우 키를 분리하고, 범위를 좁히고, 다시 측정하세요.

범위, 역할, 단명 토큰으로 키를 제한하세요

영향 범위를 정의했다면 다음 세 가지 제어를 함께 적용하세요.

1. OAuth 범위를 최소화하세요

OAuth를 사용한다면 필요한 범위만 요청합니다.

tickets.read
drafts.write
Enter fullscreen mode Exit fullscreen mode

다음과 같이 편의상 인접 권한을 함께 추가하지 마세요.

tickets.read
tickets.write
billing.read
users.manage
Enter fullscreen mode Exit fullscreen mode

예를 들어 티켓을 읽고 답장 초안을 만드는 에이전트에 필요한 권한은 다음처럼 좁게 설계할 수 있습니다.

tickets.read
drafts.write
Enter fullscreen mode Exit fullscreen mode

OAuth 범위 설계가 익숙하지 않다면 OAuth 2.0 범위 설명서를 참고하세요.

“나중에 필요할 수도 있으니” 추가하는 권한이 바로 영향 범위를 키우는 원인입니다.

2. 서버 측 역할 검사를 강제하세요

범위만으로는 충분하지 않습니다. 모든 상태 변경 엔드포인트에서 서버 측 역할 검사를 수행해야 합니다.

app.patch("/tickets/:id", requireAuth, async (req, res) => {
  if (!req.user.roles.includes("ticket_editor")) {
    return res.status(403).json({ error: "Forbidden" });
  }

  // 객체 수준 권한도 별도로 확인
  const ticket = await findTicket(req.params.id);

  if (ticket.tenantId !== req.user.tenantId) {
    return res.status(403).json({ error: "Forbidden" });
  }

  // 업데이트 수행
});
Enter fullscreen mode Exit fullscreen mode

이 구조에서는 침해된 클라이언트가 관리자 API를 요청하더라도 서버가 거부합니다. 이것이 BFLA를 줄이는 핵심입니다.

3. 단명 토큰을 사용하세요

영구 키는 공격자가 장기간 사용할 수 있는 키입니다. 가능한 경우 몇 분 또는 몇 시간 내 만료되는 단명 토큰을 사용하고, 통제된 갱신 흐름을 구성하세요.

긴 수명의 정적 API 키
→ 유출 후 수개월 동안 악용될 수 있음

짧은 수명의 액세스 토큰
→ 유출 후 제한된 시간만 악용 가능
Enter fullscreen mode Exit fullscreen mode

Bearer 토큰과 서명된 JWT는 단명 토큰 운영을 실용적으로 만듭니다. 다만 단명 토큰은 활성 세션에서의 실시간 공격을 막지는 못하며, 과도한 권한 자체를 해결하지도 않습니다.

따라서 다음을 함께 적용해야 합니다.

엄격한 범위 + 서버 역할 검사 + 객체 접근 검사 + 단명 토큰
Enter fullscreen mode Exit fullscreen mode

자격 증명은 에이전트만 읽을 수 있게 저장하세요

완벽하게 범위가 지정된 키라도 유출되면 피해가 발생할 수 있습니다. 가장 흔한 유출 경로는 특별하지 않습니다.

  • 소스 코드에 하드코딩된 토큰
  • .env 파일의 실수로 인한 커밋
  • 채팅 메시지에 붙여넣은 키
  • 공유된 요청 컬렉션에 포함된 인증 정보

자격 증명은 환경 변수 또는 전용 비밀 관리자에서 관리하고 런타임에 주입하세요.

// 하지 말아야 할 예
const API_KEY = "sk-live-actual-secret";
Enter fullscreen mode Exit fullscreen mode
// 권장 예
const API_KEY = process.env.AGENT_TICKETS_READ_TOKEN;
Enter fullscreen mode Exit fullscreen mode

여러 환경을 운영한다면 .env 파일보다 비밀 관리자가 더 적합할 수 있습니다. 관련 패턴은 API 키를 올바르게 저장하는 방법에서 확인할 수 있습니다.

Apidog에서는 토큰을 요청 정의에 직접 입력하는 대신 환경 변수로 참조할 수 있습니다. 원시 비밀을 공유 프로젝트와 버전 관리에서 분리하고, 팀원이 실제 토큰 값을 보지 않아도 동일한 요청을 실행할 수 있습니다.

다만 역할을 분명히 이해해야 합니다.

Apidog가 도울 수 있는 부분 Apidog가 대신하지 않는 부분
환경 변수로 인증 정보 참조 비밀 자동 갱신
요청별 권한 테스트 네트워크 송신 제어
권한별 테스트 시나리오 문서화 런타임 공격 탐지
낮은 권한 토큰으로 API 검증 모델 가드레일 및 방화벽

비밀 갱신, 네트워크 제어, 런타임 모니터링은 비밀 관리자, 클라우드 플랫폼, 로깅 스택에서 담당해야 합니다.

“읽기 전용” 키가 쓰기 작업을 거부하는지 테스트하세요

많은 팀이 권한을 설정한 뒤 검증하지 않습니다. 그러나 “읽기 전용”은 실제 요청이 이를 증명하기 전까지 단지 라벨일 뿐입니다.

검증 방법은 간단합니다.

  1. 실제 낮은 권한 토큰을 사용합니다.
  2. 허용되어야 하는 읽기 요청을 보냅니다.
  3. 거부되어야 하는 쓰기 및 관리자 요청을 보냅니다.
  4. 모든 금지된 요청이 401 또는 403을 반환하는지 확인합니다.
  5. 2xx 응답은 테스트 실패로 처리합니다.

예를 들어 다음 테스트 스위트를 만들 수 있습니다.

테스트 케이스 요청 사용된 토큰 예상 상태
본인 티켓 읽기 — 허용 GET /tickets/1001 에이전트 읽기 전용 200
티켓 쓰기 — 거부 PATCH /tickets/1001 에이전트 읽기 전용 401 또는 403
티켓 삭제 — 거부 DELETE /tickets/1001 에이전트 읽기 전용 401 또는 403
다른 테넌트 티켓 읽기 — BOLA 검증 GET /tickets/9999 에이전트 읽기 전용 403 또는 404
관리자 기능 호출 — BFLA 검증 POST /admin/reset 에이전트 읽기 전용 401 또는 403

테스트 코드에서는 “금지된 요청이 성공하지 않는지”를 명시적으로 검사해야 합니다.

pm.test("읽기 전용 토큰은 PATCH 요청을 거부해야 한다", () => {
  pm.expect(pm.response.code).to.be.oneOf([401, 403]);
});

pm.test("응답 본문에 객체 데이터가 포함되면 안 된다", () => {
  const body = pm.response.text();
  pm.expect(body).not.to.include("sensitive_field");
});
Enter fullscreen mode Exit fullscreen mode

403을 반환하더라도 응답 본문에 일부 레코드 데이터가 노출된다면 여전히 버그입니다. 상태 코드뿐 아니라 응답 본문도 함께 확인하세요.

이 스위트는 인증 설정, 역할 정책, API 게이트웨이 규칙이 변경될 때마다 CI에서 실행해야 합니다. 선의의 리팩터링이 권한 범위를 넓히더라도, 프로덕션 배포 전에 실패한 테스트로 발견할 수 있어야 합니다.

전체 점검 항목은 API 보안 테스트 체크리스트에서 확인할 수 있습니다. 직접 적용하려면 Apidog를 무료로 사용해 보고, 부정 권한 확인 케이스를 테스트 시나리오에 연결하세요.

단, 통과한 테스트를 과신해서는 안 됩니다. 테스트가 증명하는 것은 “시도한 특정 요청이 거부되었다”는 사실뿐입니다. 아직 테스트하지 않은 다른 경로나 기능이 안전하다는 뜻은 아닙니다. API가 성장할수록 테스트 케이스도 계속 추가해야 합니다.

이번 주에 실행할 영향 범위 체크리스트

에이전트 키를 더 안전하게 만들기 위해 별도의 보안 팀이 반드시 필요한 것은 아닙니다. 다음 작업을 실행하세요.

  • [ ] 에이전트의 작업을 한 문장으로 작성하고, 필요한 API 작업만 나열합니다.
  • [ ] 에이전트별 고유 자격 증명을 발급하고, 공유 키와 관리자 토큰을 제거합니다.
  • [ ] 접근 가능한 서비스, 읽기/쓰기/삭제 가능한 객체, 관리자 기능을 표로 정리합니다.
  • [ ] 표에 없는 “만일을 대비한” 범위를 모두 제거합니다.
  • [ ] 모든 상태 변경 엔드포인트에 서버 측 역할 검사를 추가합니다.
  • [ ] 모든 객체 조회 및 수정 요청에 테넌트 또는 소유권 검사를 적용합니다.
  • [ ] 갱신 흐름이 있는 단명 토큰으로 전환합니다.
  • [ ] 비밀을 환경 변수 또는 비밀 관리자로 옮기고 git 커밋 여부를 확인합니다.
  • [ ] 금지된 POST, PATCH, DELETE 요청이 401 또는 403을 반환하는 부정 테스트를 작성합니다.
  • [ ] 인증 또는 권한 정책 변경 시 해당 테스트를 CI에서 실행합니다.

이 과정을 거치면 “우리 에이전트의 키는 무엇을 할 수 있는가?”라는 추상적인 질문이 짧고, 문서화되어 있으며, 테스트된 답변으로 바뀝니다. 그 답변을 명확히 설명할 수 없다면, 그 키는 아직 프로덕션 에이전트에 부여할 준비가 되지 않은 것입니다.

자주 묻는 질문

AI 에이전트의 최소 권한은 구체적으로 무엇을 의미하나요?

에이전트 자격 증명에 해당 작업에 필요한 권한만 부여하고, 그 외 권한은 부여하지 않는다는 뜻입니다. 에이전트는 사람의 검토 없이 대량의 요청을 수행할 수 있으므로, 과도하게 넓은 권한은 더 빠르고 큰 피해로 이어질 수 있습니다. 범위, 역할, 객체 접근 검사를 서버에서 강제해야 합니다.

BOLA와 BFLA의 차이점은 무엇인가요?

BOLA는 데이터 객체 접근 문제입니다. 호출자가 ID를 바꿔 접근 권한이 없는 객체를 읽거나 수정할 수 있는 상황입니다.

BFLA는 기능 접근 문제입니다. 호출자가 관리자 삭제, 시스템 재설정처럼 자신의 역할을 넘어서는 기능을 호출할 수 있는 상황입니다.

둘 다 서버 측 검사가 필요합니다.

키가 실제로 읽기 전용인지 어떻게 확인하나요?

읽기 전용 키로 PATCH, POST, DELETE 요청을 직접 보내세요. 모든 요청은 401 또는 403을 반환해야 하며, 2xx 응답은 테스트 실패로 처리해야 합니다. 다른 테넌트 객체에 대한 GET 요청도 함께 테스트해 BOLA를 검증하세요.

단명 토큰만으로 충분한가요?

아니요. 단명 토큰은 유출된 자격 증명이 악용될 수 있는 시간을 줄여주지만, 과도하게 넓은 권한이나 활성 세션에서의 실시간 공격을 해결하지는 못합니다. 엄격한 범위, 서버 측 역할 검사, 객체 수준 검사, 안전한 비밀 저장과 함께 사용해야 합니다.

Apidog는 어디까지 도움이 되나요?

Apidog는 낮은 권한 토큰으로 실제 엔드포인트를 실행하고, 쓰기 요청이 401 또는 403으로 거부되는지 확인하며, 환경 변수로 인증 정보를 관리하고, 권한별 테스트를 문서화하는 데 도움이 됩니다.

반면 네트워크 방화벽, 비밀 자동 갱신, 런타임 모니터링, 모델 가드레일은 담당하지 않습니다. 이러한 제어는 클라우드 플랫폼, 비밀 관리자, 로깅 및 보안 도구와 함께 구성해야 합니다.

각 에이전트에 정말 별도 키가 필요한가요?

예. 에이전트별 자격 증명을 사용하면 문제가 있는 에이전트 하나만 해지할 수 있고, 모든 요청을 정확한 행위자에게 연결할 수 있습니다. 공유 키를 사용하면 해지 범위가 커지고, 사고 조사 시 어떤 에이전트가 어떤 요청을 수행했는지 파악하기 어려워집니다.

Top comments (0)