본문으로 건너뛰기
NEW · Blast Radius: 유출된 API 키 대응 플레이북 (AWS·GCP·Azure)
블로그로 돌아가기

목록에 없을 뿐, 비공개는 아닙니다

2026년 9월 공개 표면 33곳에서 유효한 자격증명 23,912개를 확인했습니다. 7,468건은 권한 범위를 분류했고, 1,277건에는 다른 키의 발급 권한이 있었습니다. 실제 재발급 여부는 시험하지 않았습니다.

김동현
작성자
김동현
발행
수정
8 분1,392 어절
Share:
조사 집계: 유효 자격증명 23,912건, 권한 범위를 분류한 7,468건, 다른 키의 발급 권한이 있는 1,277건

2026년 9월 9일 공개 표면 33곳에서 유효한 머신 자격증명 23,912개를 확인했습니다. 이 중 7,468건은 확인 가능한 권한 정보를 바탕으로 등급을 매겼습니다. 나머지는 권한 등급을 정하지 못했습니다. 이 글은 2026년 9월 10일 열린 제5회 CloudyMous 세미나에서 발표한 내용을 바탕으로 재정리했습니다. CloudyMous는 현업 보안 실무자가 마주한 위협과 대응 사례를 공유하는 정보보안 세미나입니다. 다음 회차 소식은 오픈채팅에서 받아보실 수 있습니다.

숨어있다고 생각한 곳이 다 열려 있었습니다

이번 조사에서 다룬 공격 표면은 대부분 '외부에 노출되지 않았다'고 무심코 넘어가는 영역들입니다. ChatGPT, Claude, Gemini, Grok의 공유 대화 링크 30,990건, GitHub 저장소의 .mcp.json 계열 에이전트 설정 파일, 그리고 AWS ECR Public의 컨테이너 이미지 레이어가 여기에 해당합니다.

이 세 곳의 공통점은 로그인 없이 누구나 내려받을 수 있다는 점입니다. 그리고 세 곳 모두에서 유효한 자격증명이 발굴되었습니다. 일례로 ECR 이미지 레이어에서는 조직 전체 러너(Runner)를 제어할 수 있는 GitHub PAT 5개가 그대로 노출되어 있었습니다.

많은 팀이 "검색 엔진에 인덱싱(목록화)되지 않았다"는 조건을 "접근이 제한되어 있다"는 보안 조건과 착각하곤 합니다. 하지만 이 둘은 다른 이야기입니다.

검증 방식과 선정 기준

사람이 로그인할 때 쓰는 계정과 달리, 코드가 시스템에 인증할 때 사용하는 API 키, 서비스 계정 토큰, OAuth 클라이언트 시크릿, DB 연결 문자열 등을 '머신 자격증명'으로 정의했습니다. 지원하는 유형의 후보는 발급처에서 유효성을 확인했습니다. 23,912건은 이 검사에서 유효하다고 확인된 자격증명입니다. 검증하지 못한 후보는 유효·무효 어느 쪽으로도 세지 않았습니다. 그중 권한 스코프까지 확인한 것은 7,468개이고, 뒤에 나오는 위험도 등급 분포는 이 7,468개를 모집단으로 합니다.

검증 질의는 모두 읽기 전용(Read-Only) 엔드포인트로만 보냈고 접근 체인을 추적하는 대신 스코프 문자열만 기록했습니다. 제3자 계정 ID와 IAM 사용자명은 기록 단계에서 마스킹했습니다. 이렇게까지 범위를 좁힌 데는 이유가 있습니다. 수집한 코퍼스 안에 카나리토큰(Canary Token) 3건이 섞여 있었습니다. 검증 질의를 보낸 그 순간 해당 토큰 소유자에게 알림이 발송되었습니다. 검증은 읽기 전용 엔드포인트에서 멈춰야 한다는 원칙을 실제 사례로 확인한 셈입니다.

공격 표면을 고를 때는 고정된 URL 목록 대신 '규칙'을 새로 정의했습니다. 새로운 AI 도구나 워크플로 도구가 자체 설정 파일 형식을 만들었을 때, 그 설정 파일의 내부 구조가 설계상 키를 담도록 되어 있는지를 기준으로 삼은 것입니다. 이에 따라 네 가지 계열이 핵심 표면으로 지정되었습니다.

  • 에이전트 설정. .mcp.json, .cursor/, .continue/, .aider.conf.yml. env 블록에 키 값을 그대로 기재하는 것이 파일 설계 자체입니다.
  • 워크플로 익스포트. n8n, Flowise, Dify, Langflow. 연결 정보를 JSON으로 그대로 내보냅니다.
  • 확장 패키지. VS Code .vsix, Chrome .crx. 계정 없이 내려받을 수 있고 스토어 검사가 번들 표면에서 멈춥니다.
  • AI 공유 링크. ChatGPT, Claude, Gemini, Grok. 대화에 입력한 값이 링크와 함께 공개됩니다.

유효 자격증명은 어디서 가장 많이 나왔나

절대적인 유효 건수로 따지면 GitHub 설정 파일이 20,777개로 가장 많았습니다. Google OAuth 클라이언트가 3,649개, GitHub 공개 저장소가 3,230개로 뒤를 이었습니다. 참고로 조사 대상 33곳 중 Common Crawl, Docker Hub, Terraform 모듈 등 11곳은 유효 건수가 0으로 나왔지만 대상에서 제외하지 않고 계속 모니터링했습니다.

다만 탐지 건수가 많다고 해서 반드시 더 위험하다고 볼 수는 없습니다. 건수 1위인 GitHub 설정 파일의 최고 심각도 사례는 북유럽 대형 보험사의 프로덕션 CMS 스페이스 16곳에 쓰기 권한이 열려 있던 건이었습니다. Supabase 스캔 표면에서는 유효한 자격증명 643개가 나왔습니다. 조사한 모든 표면을 합치면 Supabase 조직 관리 토큰 670개가 분류됐습니다. 서로 다른 집계 기준이므로 하나의 비율로 비교할 수 없습니다. MCP나 에이전트 설정의 경우 유효 건수는 31개로 작았지만 불과 2년 전에는 아예 존재하지 않던 신종 위험 경로라는 점에서 주목할 필요가 있습니다.

발급처별로 보면 인증·결제·미디어 서비스가 상위권을 차지했습니다. Google OAuth가 7,143개로 가장 많았고, Razorpay(2,261개), RapidAPI(1,094개), MercadoPago(942개), Cloudinary(932개) 순이었습니다.

여기서 눈여겨볼 점은 전체 유효 자격증명 중 904개(3.8%)가 GitHub 계열 호스트 어디에도 존재하지 않았다는 사실입니다. 특히 그중 307개는 GitHub, GitLab, Bitbucket 그 어디에서도 발견되지 않았습니다. 즉, 기존처럼 소스코드 저장소만 스캐닝하는 보안 범위로는 잡을 수 없는 노출 자산들입니다. 공격자 입장에서는 코드 저장소 밖에서도 동일하게 수집이 가능하기 때문에, GitGuardian의 State of Secrets Sprawl 2026 리포트에서도 보안 사고의 28%가 저장소 밖에서 시작된다고 강조하는 것입니다.

위험도 등급과 "키 폐기"의 허점

위험도 분류 기준은 단순 건수가 아니라, 그 키를 확보한 공격자가 어디까지 접근할 수 있는가입니다.

키의 권한이 확인된 7,468건의 위험도 등급 분포. CRITICAL 계정 소유자 1,559건, CRITICAL 공급망 185건, HIGH 실제 자금 863건, HIGH 신원 사칭 256건, MEDIUM 사내 시스템 73건, LOW 비용 소진·미디어 4,532건

수치상으로는 LOW 등급이 4,532개로 가장 많고, CRITICAL 두 종류를 합치면 1,744개 수준입니다. 새 키를 발급할 수 있는 권한은 중요한 위험 단서입니다. 기존 키로 별도 키가 이미 발급됐다면 기존 키만 폐기해도 그 별도 키는 남습니다. 이번 조사는 발급 권한을 확인했으며 실제 재발급 여부는 확인하지 않았습니다.

데이터베이스 연결 문자열은 총 6,042개가 확인되었습니다. 그중 mongodb.net 형태가 3,862개로 63.9%를 차지했습니다. mongodb+srv://user:pw@... 구조는 계정과 비밀번호를 분리해 담을 자리가 없기 때문에 한 문자열 안에 계정 정보 전체가 포함됩니다. CRITICAL 계정 소유자 등급의 다수가 DB 관리 권한으로 직행하는 이유가 여기에 있습니다.

보통 보안 사고가 터지면 대응 절차의 마지막으로 "해당 API 키를 폐기한다"를 택합니다. 이번 조사에서 유효 자격증명 1,277개에 다른 키를 발급할 수 있는 권한이 확인됐습니다. 대응할 때 발급처에서 별도 발급 기록을 확인해야 한다는 뜻입니다. 새 키가 발급됐거나 기존 키 폐기 후 접근이 유지됐다는 결과는 아닙니다. Supabase 조직 관리 토큰 670개, MongoDB atlasAdmin 488개, SendGrid 키 생성 권한 122개 등이 해당합니다. 공격자가 키를 획득하자마자 백도어용 키를 새로 발급해 두었다면, 관리자가 기존 키를 폐기해도 공격자의 접근은 계속 유지됩니다.

서로 다른 서비스의 키가 한 파일 안에 얽혀 있던 건도 1,152개에 달했습니다. 중복을 제외한 2,296건은 두 가지 추가 점검 조건 중 적어도 하나에 해당했습니다. 폐기 후 접근이 남는지 시험한 수치는 아닙니다.

유출 등록된 키가 버젓이 인증되는 기현상

알리바바 클라우드 키 172개를 검증하는 과정에서는 기이한 현상이 관찰되었습니다. 148개의 키에서 RAM 엔드포인트(GetAccountAlias)로 질의를 보내면 403 Forbidden과 함께 "유출 위험이 등록된 AccessKey"라는 응답이 돌아왔습니다. 이미 해당 키가 발급처 내부에서 유출된 것으로 식별되었다는 뜻입니다.

하지만 같은 키로 STS 엔드포인트(GetCallerIdentity)를 호출하자 200 OK 정상 응답이 돌아왔습니다. 유출을 탐지하는 내부 경로와 접근을 차단하는 IAM 경로가 분리 운영되고 있기 때문입니다. 결과적으로 시스템상 '유출로 등록된 키'임에도 불구하고 공격자는 같은 키로 계속해서 시스템 인증에 성공할 수 있었습니다.

이러한 유출 키 하나가 가져오는 파급력은 상당합니다.

자격증명 권한에서 추정한 접근 가능 경로 다섯 가지. 실제 접근이나 새 키 발급을 시험한 결과는 아님

.mcp.json에서 유출된 Supabase 관리 토큰 670개는 권한상 1,211개 조직과 2,607개 프로젝트에 접근할 가능성이 있었습니다. Atlassian 관리 토큰 30개에서는 내부 Confluence 공간에 접근 가능한 권한이 확인됐습니다. 이번 조사에서 그 공간을 열어 보거나 런북 속 다른 자격증명을 확인하지는 않았습니다.

다만 이 도달 범위는 권한 스코프로 확인한 값이며 그 경로를 따라 접근을 시도하지는 않았습니다.

AI 도구가 더한 새로운 유출 경로와 .gitignore의 한계

최근 유출 트렌드는 AI 도구의 등장으로 인해 급격히 변화하고 있습니다. 기존 GitHub 저장소 내부지만 2년 전에는 없던 새로운 경로들이 생겨났습니다.

  • .mcp.json 308개
  • .cursor/mcp.json 302개
  • .vscode/mcp.json 144개
  • .gemini/settings.json 43개
  • .claude/settings.local.json 43개
  • .kiro/settings/mcp.json 31개
  • .kilocode/mcp.json 28개

AI 에이전트 설정 파일에서 나온 유효 키만 1,248개로 전체의 5.3%를 차지했습니다. 또한 AI/LLM 관련 API 키(Pinecone 609개, Tavily 567개, Context7 522개 등)가 2,998개(12.5%)나 발굴되며 유출되는 키의 종류 자체가 AI 생태계로 이동하고 있음을 보여주었습니다. 반면 AI 공유 대화 링크는 30,990건 중 자격증명이 포함된 비율이 0.384%(Wilson 95% 신뢰구간 0.321~0.459%)입니다. 화제성에 비해 실제 유출 비중은 작았습니다.

문제는 개발팀이 작성하는 .gitignore가 이 속도를 따라가지 못한다는 점입니다.

저장소 2,537개의 .gitignore 전수 확인 결과. .env를 제외한 비율 76.9%, 에이전트 디렉터리를 언급만 한 비율 62.3%, 유출 파일까지 덮은 비율 9.3%, 언급했으나 덮지 못한 비율 53.1%

전형적인 실수는 제외 규칙에 .claude/ 같은 디렉터리 이름만 적어두는 경우입니다. 실제 유출 파일명인 .claude/settings.local.json까지 덮지 못해 53.1%의 저장소가 설정 파일을 그대로 외부에 노출시켰습니다. 더욱이 유출 파일 4,049건 중 89.8%가 커밋이 올라온 당일에 공개되었습니다. 중앙값은 0일, 평균은 1.91일이었습니다. 게시되는 순간 수집봇이 가져가므로 .gitignore 규칙이 작동할 여유 시간 자체가 존재하지 않는 것입니다.

유출 파일 4,049건의 게시 시점 분포. 당일 게시가 89.8%인 3,636건이고 1일 4.8%, 2~7일 2.4%로 줄어듭니다. 중앙값 0일, 평균 1.91일

대응과 검증의 한계

조직 입장에서 노출을 차단하려면 다음 순서를 권합니다.

  1. `git log`로 이미 추적 중인 파일부터 확인합니다. .gitignore는 이미 커밋된 파일에 소급 적용되지 않습니다.
  2. 설정 파일 내 키 값을 환경변수 참조로 바꿉니다. 디렉터리 이름만 적고 실제 유출 파일을 빠뜨린 저장소가 53.1%였습니다.
  3. Key Rotation 시 재발급 권한을 함께 점검합니다. 단순 폐기에 그치지 않고, 그 키로 발급된 하위 계정이나 추가 키가 없는지 확인해야 합니다.
  4. 점검 범위를 Git 저장소 밖으로 넓힙니다. 패키지 레지스트리, 컨테이너 이미지 레이어, 확장 마켓플레이스가 대상입니다. 내부 접근 없이 공격자 시점에서 외부 자산을 훑는 외부 노출 스캐닝이 이 범위를 담당합니다.
  5. Pre-commit 게이트를 도입합니다. 유출의 89.8%가 게시 당일에 공개되는 만큼, 커밋 전 단계를 막아야 합니다.

조사 과정에서의 한계점도 명확했습니다. 유효성 질의 시 무효로 판정된 60,865건 중 공식적으로 '폐기됨'이 명시된 건은 12.4%(7,535건)에 불과했습니다. 나머지 75.3%는 원인을 알 수 없는 401/403 에러나 무응답이었습니다. 발급처가 키 무효화 원인을 응답에 담는지 여부도 향후 서비스 선택의 기준이 되어야 함을 시사합니다.

소유 조직을 특정하는 일은 더 어려웠습니다. 유효 자격증명 23,681개 중 발급처가 계정 메일 주소를 돌려준 것이 1,566개, 그 주소가 회사 도메인인 것이 309개였습니다. 여기에 네 단계 검증을 더 적용하자 최종 확인된 건은 3개까지 줄었습니다.

조직 귀속 검증 퍼널. 유효 자격증명 23,681건에서 계정 메일 반환 1,566건, 회사 도메인 309건, 네임스페이스 일치 25건을 거쳐 최종 3건까지 좁혀진다

가장 많이 탈락한 구간은 309개에서 25개로 줄어든 지점입니다. 회사 도메인 메일 주소는 그 계정을 만든 사람이 누구인지를 알려줄 뿐, 그 자산이 회사 소유라는 증거가 되지는 못합니다. 계정 설정에 적힌 회사명, 조직 이름, 워크스페이스 제목도 누구나 임의로 적을 수 있는 값이라 근거로 쓸 수 없습니다. 이 조사에서도 초안에는 "306개가 회사 자산이 아니었다"고 쓸 뻔했습니다. 검증을 통과하지 못한 것과 회사 자산이 아닌 것은 서로 다른 이야기입니다.

측정하지 못한 부분을 0으로 적지 않는다는 원칙으로 조사를 진행했기에 빈칸도 그대로 남겼습니다. 권한 스코프를 확인하지 못한 16,444개, 무효화 원인을 알 수 없는 75.3%가 남았습니다. 이 빈칸까지 채워 넣으면 숫자는 더 커지지, 작아지지는 않습니다.

이번 조사가 확인한 것은 하나입니다. 목록에 올라 있지 않다는 조건은 접근이 제한되어 있다는 조건과 다릅니다. 공유 링크도, 에이전트 설정 파일도, 컨테이너 이미지 레이어도 누구의 눈에도 띄지 않는다는 전제 위에서 운영되고 있었습니다. 그러나 로그인 한 번 없이 23,912개의 살아 있는 키를 내주고 있었습니다.

네트워크에 공유해보세요LinkedInX

이어서 읽기

다음 글을 메일로 받아보세요

Cremit의 월간 자격증명 보안 브리프. 한 통에 핵심만 담습니다.

이메일을 판매하거나 외부에 넘기지 않으며, 언제든 수신거부할 수 있습니다.

유효 자격증명 23,912개의 영향 범위 | Cremit