Vercel 2026년 4월 보안 사고, 결국 NHI 문제입니다: 무엇부터 교체해야 하는가
2026년 4월 19일 Vercel이 공식 확인한 사고는 한 직원이 쓰던 서드파티 AI 도구에서 시작돼 Google Workspace를 거쳐 Vercel 내부 시스템에 도달했고, 일부 고객 프로젝트의 환경 변수 중 sensitive 플래그가 없던 값들이 노출됐습니다. 확인된 사실과 루머를 나누고, 지금 무엇부터 교체해야 하는지 정리했습니다.
Share:
목차(10)
목차
다른 곳에서 시작된 사고
2026년 4월 19일, Vercel이 자사 일부 내부 시스템에 무단 접근이 있었다는 공지를 올렸습니다. 그 과정에서 일부 고객 프로젝트의 환경 변수에도 접근이 있었다고 합니다. 공지에서 주목할 부분은 경로입니다. 이 사고는 Vercel 내부에서 시작된 게 아닙니다. Vercel 직원 한 명이 쓰던 서드파티 AI 도구가 먼저 뚫렸고, 그걸 발판으로 공격자가 해당 직원의 Google Workspace 계정을 가져갔으며, 그 계정으로 Vercel 내부 시스템까지 들어왔습니다.
이게 지금 의사결정에 필요한 전부입니다. 이번 주말 다크웹 포럼과 텔레그램을 떠돌고 있는 ShinyHunters 브랜딩, BreachForums-dot-ai 게시물, 2백만 달러 랜섬 요구 스크린샷, 직원 패널 탈취설 같은 이야기들은 대체로 미확인이거나 반박돼 있습니다. 이 부분은 뒤쪽에서 따로 정리하겠습니다.
대부분의 팀에게 지금 던져야 하는 질문은 "Vercel이 뚫렸는가"가 아닙니다. 그건 Vercel이 이미 확인했습니다. 실제로 답해야 하는 건 "내 Vercel 환경 변수 중 sensitive 플래그가 안 붙어 있던 값이 뭐였지"입니다. 그 값들은 지금 전부 신뢰할 수 없는 상태입니다.
아래에서는 확인된 사실, 공격 경로, sensitive 플래그가 왜 이 사고의 피해 범위를 가르는지, 이게 NHI Kill Chain의 어디에 맞물리는지, 그리고 지금 어떤 순서로 교체해야 하는지를 차례대로 봅니다. 프로덕션 Vercel 프로젝트가 있는데 어떤 env var가 sensitive인지 바로 떠오르지 않으면, 아래 교체 플레이북부터 먼저 보셔도 됩니다.
확인된 사실과 아직 루머인 것
키 교체를 시작하기 전에 이 두 쪽을 구분해두는 게 낫습니다. 의사결정은 앞쪽 목록에서 나와야 하고, 뒤쪽 목록에서 나오면 안 됩니다.
Vercel 공식 공지로 확인된 사실 (2026-04-19~20 기준):
Vercel 일부 내부 시스템에 대한 무단 접근이 발생했습니다.
접근 경로는 직원이 사용하던 서드파티 AI 도구 Context.ai의 침해에서 시작됐습니다.
공격자는 Context.ai 침해를 통해 해당 직원의 Google Workspace 계정을 탈취했습니다.
"sensitive"로 표시되지 않은 일부 고객 환경 변수에 접근이 있었습니다.
Sensitive Environment Variable로 저장된 값이 접근되었다는 증거는 없다고 Vercel은 밝혔습니다.
영향받은 고객은 소수이며, Vercel이 개별적으로 직접 연락하고 있습니다.
권고 조치는 sensitive 플래그가 없던 환경 변수 교체, 향후 sensitive 플래그 활성화, 활동 로그 검토, Deployment Protection 강화와 해당 토큰 교체입니다.
현재까지 미확인인 것:
BreachForums-dot-ai의 "ShinyHunters" 핸들 계정이 이번 사고를 최초로 포스팅했습니다. 이 계정이 실제 ShinyHunters 탈취 그룹인지는 논란이 있습니다. ShinyHunters 측으로 추정되는 텔레그램 메시지는 본인들의 소행이 아니며 해당 계정은 사칭이라고 주장했습니다.
BreachForums-dot-ai 자체가 논란 대상입니다. BreachForums와 RaidForums는 수차례 폐쇄와 운영권 이전을 거쳤고, 지금 "진짜" 후계 포럼이 어느 인스턴스인지가 커뮤니티 논쟁의 일부입니다.
2백만 달러 랜섬 요구 스크린샷이 유포되고 있습니다. 진위는 확인되지 않았습니다.
초기 접근 경로가 Vercel 직원 패널 탈취라는 추측도 있습니다. 다만 이는 Google Workspace 탈취 이후의 측면 이동 과정에서 촬영된 스크린샷일 수도 있습니다. Context.ai 이후의 초기 벡터는 공개된 정보로는 분명하지 않습니다.
"소수 고객"의 구체적인 수치는 공개되지 않았습니다. Vercel 전체 고객 규모를 고려하면, "소수"라는 표현이 절대 수치로는 작지 않을 수 있다는 점은 지적할 만합니다.
첫 번째 목록이 지금 의사결정의 근거가 되어야 합니다. 두 번째 목록은 흥미롭고 저희도 무시하지 않지만, 그게 정리될 때까지 대응을 미루면 안 됩니다. 공격자는 이야기가 정리되기 전에 먼저 돈을 챙깁니다.
확인된 공격 경로
Vercel이 밝힌 경로는 짧습니다. 짧은 대신 시사점이 분명합니다. 이 사고 이후 실제로 누가 행동을 바꿔야 하는지, 한 단계씩 풀어보면 보입니다.
Vercel 2026년 4월 사고 공격 경로: Context.ai 침해에서 Google Workspace 탈취, Vercel 내부 접근, non-sensitive 환경 변수 조회까지의 4단계 체인
먼저 Context.ai가 뚫렸습니다. Context.ai는 Vercel 직원 한 명이 개인 생산성 워크플로에서 쓰던 AI 제품입니다. 이 도구가 해당 직원의 Google Workspace에 가지고 있던 접근 권한이, Context.ai가 뚫리는 순간 그대로 공격자의 것이 됐습니다.
이어서 공격자는 그 접근을 이용해 직원의 Google Workspace 계정 자체를 탈취합니다. 외부 침해가 내부 침해로 넘어가는 지점이 여기입니다. 이 시점부터 공격자는 Vercel 내부 시스템이 신뢰하는 신원 안쪽에 앉아 있습니다.
마지막으로 그 신원을 이용해 Vercel 내부 시스템으로 들어가, non-sensitive로 저장된 고객 환경 변수를 조회합니다.
이 사슬의 핵심은 하나입니다. 공격이 Vercel 제품 경계를 뚫은 게 아니라, 신원 경계를 넘었다는 것. 그리고 그 신원은 서드파티 AI 도구를 개인적으로 도입한 한 직원의 것이었습니다. MoltBot, Cline/Clinejection 공급망 연구에서 짚었던 것과 같은 패턴입니다. 생산성용 AI 에이전트는 자신을 쓰는 사람의 접근 권한을 그대로 물려받기 때문에, 명시적으로 허가받은 적 없는 시스템으로 들어가는 발판이 됩니다.
조직 내 누군가가 업무 이메일이나 캘린더, 문서, 또는 ID 제공자에 연결된 AI 도구를 쓰고 있다면, Vercel과 무관하게 지금 점검해야 할 위험의 형태가 바로 이것입니다.
"sensitive"라는 단어가 사실상 모든 일을 하고 있습니다
Vercel 플랫폼에는 환경 변수를 저장하는 방식이 둘입니다. 대시보드에서는 똑같이 보이지만, 실제로는 같은 것이 아닙니다.
일반 환경 변수는 값을 읽을 수 있는 형태로 저장됩니다. 빌드, 서버리스 함수, 대시보드가 값을 써야 하기 때문입니다. 대부분의 플랫폼이 환경 변수를 이렇게 다루고, Vercel에서도 변수를 새로 추가할 때의 기본값이 이쪽입니다.
Sensitive Environment Variable은 저장소에서 암호화된 상태로 들어가고, 값을 다시 꺼낼 수 없습니다. 대시보드에서도 못 읽고, 관리자도 못 읽고, 결정적으로 Vercel 내부 시스템에 무단 접근한 외부 공격자도 못 읽습니다. 값은 실행 시점에 빌드나 함수로 주입되고, 그 뒤로는 재조회가 안 됩니다.
Vercel 환경 변수: non-sensitive는 읽기 가능하고 이번 사고의 노출 범위 안쪽, sensitive는 암호화되어 재조회 불가
요지는 공격자가 도달한 곳이 non-sensitive 환경 변수였다는 것입니다. Vercel은 sensitive 환경 변수가 접근됐다는 증거는 없다고 밝혔고, 저장 방식을 생각하면 이건 희망적 추측이 아니라 플랫폼 설계상 자연스러운 결과입니다.
많은 팀이 이 글을 읽으면서 뼈아프게 느끼는 지점은, 얼마나 많은 시크릿이 non-sensitive 쪽에 쌓여 있었는지 깨닫는 순간입니다. 누가 "이건 sensitive 안 해도 돼"라고 결정해서 그런 게 아니라, 그냥 sensitive 플래그가 기본값이 아니고 아무도 나중에 되돌아가서 플래그를 손보지 않았기 때문입니다. 클라우드 환경에서 SSRF가 메타데이터 엔드포인트에 닿는 이유와 똑같은 구조입니다. 설계 결함이 아니라, 한 번 잘 작동한 기본값이 다시 검토되지 않은 결과입니다.
교체 결정은 그래서 복잡할 필요가 없습니다. Vercel 프로젝트에 있던 자격증명 중 sensitive로 표시되지 않은 값이 있다면, 전부 노출됐다고 보고 교체하세요. 해당 프로젝트가 직접 통보받은 고객군에 포함됐는지 여부는 사고 보고의 문제이지, 교체 여부의 문제가 아닙니다.
참고로, 저희는 정확히 1년 전인 2025년 4월에 Vercel 환경 변수 시크릿 노출 사례 분석을 공개한 적이 있습니다. 그때 다룬 건 이번과 다른 각도였습니다. NEXT_PUBLIC_ 접두사 오용으로 서버 전용 시크릿이 클라이언트 번들에 실리는 패턴, 그리고 공개된 Vercel 호스팅에서 살아 있는 시크릿이 실제로 발견되는 비율에 관한 이야기였습니다. 개발자 실수 레이어의 문제였고, 플랫폼 레이어의 문제는 아니었습니다. 이번 주말의 사고는 같은 "Vercel 환경 변수가 사실상 NHI 저장소"라는 전제를, 이번에는 플랫폼 레이어에서 한 번 더 확인시켜 줍니다.
Vercel의 문제가 아니라 NHI Kill Chain의 문제입니다
Cremit은 Non-Human Identity 보안 패턴을 다루는 Kill Chain 시리즈를 연재해왔는데, 이번 사고는 그 시리즈에 등장하는 네 가지 패턴을 거의 한 번에 보여줍니다.
Over-shared Key. API 키 하나, DB 자격증명 하나가 Vercel 프로젝트 환경 변수에 들어가 있는 동시에 CI 파이프라인에도, 스테이징에도, 누군가의 로컬 개발 컨테이너에도 같이 박혀 있습니다. 한 곳에서 교체해도 나머지는 그대로입니다. 피해 범위는 "이 프로젝트"가 아니라 "같은 값이 박혀 있는 모든 곳"입니다. Over-shared Key 포스트에서 다룬 구조입니다. 여러분 시크릿의 지배적 형태가 이 쪽이라면, 이번 주는 짧은 교체가 아니라 긴 교체 사이클이 됩니다.
Out of Scope Loophole. 여러분의 시크릿 매니저 인벤토리에는 시크릿 매니저에 들어 있는 것만 잡힙니다. Vercel 프로젝트 환경 변수, 또는 일반적으로 서드파티 PaaS의 환경 변수는 인벤토리에 올라와 있지 않은 경우가 훨씬 많습니다. 어느 개발자가 배포를 풀기 위해 새벽에 추가했기 때문입니다. 로테이션 자동화 도구는 거기까지 닿지 않고, 보안팀은 그게 존재한다는 사실조차 모릅니다. Out of Scope Loophole 포스트에서 다룬 패턴이며, 많은 팀의 이번 주가 필요 이상으로 힘들어지게 되는 가장 큰 이유입니다.
Aged Key. 교체 주기가 없는 장기 생존 API 키는 유출됐을 때의 유효 기간을 그대로 늘려 줍니다. 2023년부터 Vercel env var에 박혀 있던 값은 단지 "유출된 자격증명 하나"가 아닙니다. 그 자격증명이 접근할 수 있는 모든 리소스에, 여러분이 사실을 알아채기 전까지의 전 기간 동안, 공격자가 접근할 수 있다는 뜻입니다. 시간이 피해 규모의 숨은 변수라는 점은 Aged Key에서 다뤘습니다.
Ghost Key. 이미 쓰지 않는 서비스를 위해 프로젝트에 남아 있던 환경 변수. 서비스는 사라졌지만 키는 여전히 인증됩니다. 우리가 연동을 해지했다는 사실은 공격자 입장에서 아무 의미가 없습니다.
Kill Chain 시리즈를 읽으시면서 "이론적이다"고 느끼셨다면, 이번 사고가 그 구체적 사례입니다. 평소에는 잠재돼 있던 네 가지 문제를 플랫폼 사고 하나가 같은 주에 반드시 손봐야 하는 일로 밀어 올립니다.
다크웹 쪽 소음
앞서 미루었던 다크웹 포럼 쪽 이야기입니다.
BreachForums-dot-ai에 있는 ShinyHunters 핸들 계정이 Vercel 사고를 처음 포스팅했습니다. 반면에 ShinyHunters 본인들로 추정되는 텔레그램 메시지는 "우리 소행 아니다, 그 계정은 사칭이다"라고 선을 그었습니다. 두 주장 모두 지금 외부에서 검증할 방법이 없습니다. BreachForums-dot-ai가 과거 BreachForums 인스턴스들의 "진짜" 후계인지도 논쟁 중인데, 이 계열 포럼의 폐쇄와 재개 이력을 생각하면 그 논쟁은 자연스럽습니다. 2백만 달러 랜섬 요구 스크린샷도 돌고 있지만 진위는 확인되지 않았습니다.
이 중 어느 것도 여러분이 해야 할 일을 바꾸지 않습니다. "정말 ShinyHunters인가"는 책임을 누구에게 묻느냐의 문제이며, 연구자와 수사 기관이 몇 주에 걸쳐 붙잡고 갈 부분입니다. "내 non-sensitive 환경 변수를 교체해야 하는가"라는 질문의 답은 지금 어느 행위자가 데이터를 쥐고 있든 같습니다.
이 섹션을 따로 둔 건 소음을 못 본 척하고 싶지 않아서입니다. 다만 의사결정은 확인된 사실로 하세요.
교체 플레이북
이 순서로 진행하세요. 피해 범위가 큰 것부터, 그다음 표면적, 마지막으로 위생입니다. 감사부터 하고 교체하지 마세요. 프로덕션에 닿는 값부터 먼저 교체하고, 감사는 병행합니다.
사고 이후 키 교체 우선순위 사다리: 프로덕션 DB, 쓰기 권한 API 키, 서명 시크릿, Deployment Protection 토큰, 읽기 전용 API 키, 내부 전용 토큰 순
T+0, 오늘.
소유한 모든 Vercel 프로젝트에서 sensitive로 표시되지 않은 환경 변수 목록을 뽑으세요. 프로젝트 수가 많다면 대시보드를 클릭하지 말고 Vercel API로 스크립트를 돌리세요.
다음 순서로 교체합니다.
프로덕션 DB 자격증명, 결제 처리 API 키.
쓰기 권한이 있는 외부 API 키 (클라우드 제공사, 이메일 발송, 스토리지 버킷, 코드 호스트, 메신저 플랫폼).
서명 키, JWT 서명 시크릿, 웹훅 서명 시크릿.
프로덕션 연동의 OAuth 클라이언트 시크릿.
교체할 때마다 같은 값을 복사해 둔 다운스트림 시스템을 함께 업데이트하세요. Over-shared Key 패턴이 여기서 발목을 잡고, 사고도 여기서 길어집니다.
T+0 ~ T+1.
새 값을 등록할 때 sensitive 플래그를 반드시 켜세요. 이 단계는 건너뛰지 마세요. 새 값이 sensitive로 저장된 상태로 재배포되는 순간, 다음 사고에 대한 고리가 닫힙니다.
공개 환경이나 프로덕션 환경에는 Deployment Protection을 최소 Standard 이상으로 설정하세요. 사고 시점에 존재했던 Deployment Protection 토큰도 함께 교체합니다.
사고 시점에 해당하는 활동 로그를 확인하세요 (Vercel 공지에 해당 기간이 포함되어 있습니다). 이상한 배포, 이상한 환경 변수 조회, 이상한 팀 멤버십/토큰 변경이 있는지 보세요. 증명이 아니라 이상 징후를 찾는 작업입니다.
T+2 ~ T+7.
다운스트림 시스템에서 옛 값의 잔재를 감사하세요. CI 러너, 프리뷰 환경, 레포 포크, 내부 개발자 도구, 운영 런북, 일회성 스크립트가 대상입니다. Out of Scope Loophole이 아프게 작동하는 구간입니다. 교체된 값이 어딘가 한 군데라도 복사돼 남아 있다면, 그 값은 여전히 유효한 자격증명입니다.
T+0 목록에 들지 못한 읽기 전용 API 키도 이 시점에 교체하세요. "읽기 전용"은 상대적인 단어입니다. 적대적 행위자의 손에 있을 때 곤란할 만큼의 조회·추출 권한이 포함돼 있는 경우가 많습니다.
지속적.
교체된 값이 공개된 어딘가에 다시 등장하지 않는지 모니터링하세요. GitHub 커밋, gist, 포크, paste 사이트, 컨테이너 이미지, 빌드 로그가 대상입니다. "이번 교체가 충분했는가"의 진짜 테스트는 이후 90일 안에 옛 값이 공개 어딘가에서 발견되는지입니다.
정책을 다시 검토하세요. 어느 범주의 시크릿이 기본값으로 sensitive여야 하는지. 대부분의 팀에 대한 답은 "유출됐을 때 교체해야 하는 모든 값"이며, 실제로는 거의 전부입니다.
이 목록에서 하나만 할 수 있다면, 1번부터 4번까지만이라도 하세요.
패턴은 Vercel보다 큽니다
지금까지 같은 모양의 사고를 세 건 다뤘습니다. 회사 내부 누군가가 도입한 생산성 계층의 AI 도구가, 결과적으로 그 도구에 노출돼서는 안 됐던 자격증명을 쥐거나 거기에 닿게 되는 구조입니다. MoltBot, 그리고 Cline/Clinejection, 이번 Context.ai가 세 번째입니다.
공통분모는 AI 도구가 특별히 더 위험하다는 것이 아닙니다. 한 직원 신원의 표면적을 보안 리뷰 속도보다 빠르게 벌려 놓는다는 점, 그리고 그 확장의 대부분이 보안팀 가시성이 약한 개인 생산성 계정 안에서 일어난다는 점이 문제입니다. 직원이 어떤 AI 도구에 Workspace, 메일함, 코드의 읽기/쓰기 권한을 주는 순간, 그 도구는 해당 계정에서 닿을 수 있는 모든 자격증명을 상속받습니다. 공격자에게도 그 권한은 같은 수준으로 유용합니다.
이 프레임은 "AI Agents Are Creating NHIs at Scale" 글에서 다뤘습니다. 이번 주말의 Vercel 사고는 거기서 말한 추상적인 이야기가 현실에서 어떻게 생겼는지 보여주는 사례일 뿐입니다.
Cremit이 볼 수 있는 것과 볼 수 없는 것
NHI 시장에는 실제 사고 앞에서 깨지는 주장을 하는 벤더가 많습니다. 그래서 할 수 없는 것과 지금 하고 있는 것, 준비 중인 것을 나눠서 적습니다.
지금 이 순간, Vercel 내부 환경 변수 저장소 자체를 저희가 읽을 수는 없습니다. Vercel 외부의 누구도 못 합니다. "내 특정 키가 내 Vercel 프로젝트에서 실제로 읽혔는가"를 묻는다면, 그 답은 Cremit에서도, 다른 외부 스캐너에서도 나오지 않습니다.
저희가 하는 일은 그 반대편입니다. Cremit의 Argus는 코드, 포크, CI 로그, 공개 표면에 노출된 시크릿을 탐지합니다. 여기서 더 나아가, HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager 같은 시크릿 저장소와는 직접 연동해서 "어디에 무엇이 저장돼 있는지"를 인벤토리에 올려놓는 작업을 붙여 가고 있습니다. 같은 값이 Vault에도 있고 공개 레포에도 흘러나갔다면, 그 연결은 같은 화면에서 잡습니다.
Vercel처럼 환경 변수를 저장하는 PaaS 쪽 연동은 저희 로드맵에 올라와 있습니다. 이번 사고처럼 "Vault도 아니고 코드도 아닌, PaaS env var에 박혀 있던 값이 문제였다"는 케이스를 장기적으로 같은 인벤토리 위에서 다루기 위해서입니다. 시점은 약속드리지 않지만, 방향은 그쪽입니다.
Over-shared Key가 실패하는 지점이 바로 여기입니다. 그리고 대부분의 팀이 사고 후 다음 한 주를 까먹는 지점이기도 합니다. Vercel 한 곳에서 교체해도 같은 값이 Vault에, CI에, 포크에, 개인 GitHub에 클론된 내부 레포에, 유출된 컨테이너 이미지에, 빌드 로그에 남아 있다면 교체가 끝난 게 아닙니다. Argus가 만드는 인벤토리는 그걸 붙잡아주기 위한 것입니다.
사고 대응 기간이 곧 키 교체 기간입니다. 범인 추정, 포럼 소동, 랜섬 스크린샷은 지켜볼 가치는 있지만 지금 당장 해야 할 일은 아닙니다. 당장 해야 할 일은 sensitive 플래그가 없는 환경 변수를 찾고, 프로덕션에 닿는 값부터 교체하고, 교체된 값에는 sensitive 플래그를 켜고, 그다음 한 주 동안 Vercel 바깥에 흩어진 복사본들을 정리하는 것입니다.
이번 글에서 하나만 가져가신다면 이겁니다. 대부분의 플랫폼에서 기본값은 "읽기 가능"이고, 키 교체 자동화는 시크릿이 흩어져 있는 자리까지 잘 닿지 않으며, 앱을 가장 빨리 출시하는 방법은 예나 지금이나 "env vars에 붙여넣기"였습니다. 이런 사고가 그 간극을 조금씩 메워 갑니다. sensitive 플래그 한 개씩.