Firebase browser key를 제한 없이 사용하다가 13시간 만에 €54,000 청구 폭탄 맞은 사례
€54k spike in 13h from unrestricted Firebase browser key accessing Gemini APIs
TL;DR Highlight
Firebase AI Logic(Gemini API)을 활성화한 직후 제한 없는 API 키가 자동화된 공격에 악용되어 13시간 만에 €54,000이 청구됐는데, Google이 환불을 거부한 실제 사고 사례다. API 키를 클라이언트 사이드에 노출하면 얼마나 위험한지를 보여주는 경고 사례.
Who Should Read
Firebase 또는 Google Cloud를 이용해 Gemini API 기반 기능을 개발하거나 출시하려는 프론트엔드/풀스택 개발자. 특히 API 키를 클라이언트 코드에 포함시키거나 예산 관리를 제대로 설정하지 않은 소규모 팀이나 개인 개발자.
Core Mechanics
- 1년 전에 Firebase Authentication 용도로만 만든 프로젝트에 최근 Gemini API 기반 AI 기능(텍스트 프롬프트로 웹 스니펫 생성)을 추가하면서 Firebase AI Logic을 활성화했는데, 활성화 직후부터 자동화된 트래픽이 폭발적으로 증가했다.
- Firebase 프로젝트의 브라우저용 API 키는 원래 'API 제한 없음' 상태로 생성되는데, 여기에 Gemini API가 추가되자 해당 키로 Gemini 요청이 무제한으로 들어오기 시작했다. 실제 사용자 트래픽과는 전혀 무관한 자동화된 봇 요청이었다.
- €80 예산 경고와 비용 이상 알림을 설정해뒀지만, 둘 다 몇 시간 지연 후에 발송됐다. 반응했을 때는 이미 약 €28,000이 청구된 상태였고, 지연된 비용 보고로 인해 최종 금액은 €54,000 이상으로 확정됐다.
- Google Cloud 지원팀에 로그와 분석 결과를 제출했지만, '해당 사용이 해당 프로젝트에서 발생했다'는 이유로 유효한 사용으로 분류되어 청구 금액 조정 요청이 거부됐다.
- Gemini 팀 책임자(Logan Kilpatrick)가 직접 댓글로 달려서 현재 대응책을 설명했다: Tier 1 사용자는 기본적으로 월 $250 소비 한도가 있고, 프로젝트별 소비 한도(project spend caps)도 설정 가능하다. 단, 두 기능 모두 최대 10분의 지연이 존재한다.
- Google은 앞으로 제한 없는 API 키(unrestricted API key)로 Gemini API를 사용하는 것을 막는 방향으로 이동 중이라고 밝혔다. 또한 신규 사용자에게는 기본적으로 더 안전한 Auth 키를 생성해주도록 변경했다고 한다.
- 선불 결제(prepaid billing) 방식도 도입 중이라고 발표됐다. 미국 신규 사용자부터 적용됐으며 전 세계로 확대 중인데, 이를 통해 개발자가 실제로 쓸 비용을 미리 파악하고 제어할 수 있게 된다.
- Google은 원래 10년 이상 'Google API 키는 비밀이 아니다(공개해도 된다)'는 기조를 유지해왔는데, Gemini API 출시 이후 이 원칙이 깨졌다. 하지만 많은 개발자들이 이 변화를 인지하지 못한 채 기존 방식대로 키를 사용하고 있다.
Evidence
- 댓글에서 GCP 예산 알림의 구조적 문제가 집중적으로 비판받았다. 청구 이벤트가 큐/로그를 거쳐 집계되기 때문에 알림 자체가 수 시간씩 지연될 수 있는데, 설령 하드 한도를 설정해도 마지막으로 알려진 집계값 기준으로 작동하기 때문에 순간적인 스파이크에는 속수무책이라는 지적이 있었다. '이 구조는 고객이 아니라 회사를 보호하는 설계'라는 비판이 많은 공감을 얻었다.
- 비슷한 피해를 입었다는 다른 개발자도 댓글에 등장했다. $26,000 피해를 입은 후 Google Cloud 지원팀에 환불을 요청했는데 처음에는 거부됐지만 현재 재검토 중이라며, '가능한 한 위로 에스컬레이션하라'고 조언했다. 또 다른 댓글에서는 Google 공개 라이브 트레이닝 세션 중 빠르게 생성한 API 키가 유출되어 약 $6,909가 청구됐다는 사례도 공유됐다.
- GitHub에 하드코딩된 Gemini API 키가 이미 매우 많다는 지적이 나왔다. GitHub에서 'gemini "AIza"'로 검색하면 수많은 결과가 나오는데, 기존 Google Maps/Firebase 등의 키는 공개해도 안전하다는 오랜 관행 탓에 개발자들이 Gemini 키도 같은 방식으로 다루고 있다는 분석이었다.
- GCP에서 청구를 즉시 차단하는 '비상 브레이크' 기능이 공식 문서에 있다는 정보가 공유됐다. billing 계정을 프로젝트에서 분리하면 즉시 API 사용이 멈추지만, 연결된 리소스가 삭제될 수 있는 위험이 있어 프로덕션 앱에는 적합하지 않다는 주의사항도 함께 전달됐다.
- Backblaze B2를 사용 중인 개발자가 '실제로 작동하는 소비 한도'의 사례로 언급했다. B2는 API 요청에 $0 한도를 설정하면 free tier 초과 시 즉시 요청이 차단된다는 경험을 공유하며, 클라우드 서비스의 청구 설계가 본질적으로 바뀌어야 한다는 점을 강조했다.
- 사기꾼들이 왜 AI API 키를 노리는지에 대한 질문도 나왔다. EC2 자격증명으로 비트코인 채굴하는 것처럼 명확한 금전적 이득이 뭔지 모르겠다는 의문에 대해, '다른 사람 계정으로 LLM 서비스를 무료로 사용하거나 재판매하기 위한 것일 수 있다'는 추측이 제기됐다.
How to Apply
- Firebase 프로젝트에 Gemini API를 연동할 예정이라면, API 키 설정에서 반드시 'HTTP referrer 제한'과 '허용 API 제한(generativelanguage.googleapis.com만 허용)'을 동시에 설정해야 한다. 이 두 가지 제한이 없으면 키가 노출됐을 때 모든 Google 서비스에 대한 호출이 가능해진다.
- Gemini API 관련 호출은 클라이언트 사이드(브라우저, 앱)가 아닌 서버 사이드에서만 이루어지도록 아키텍처를 설계해야 한다. 불가피하게 클라이언트에서 호출해야 한다면 Firebase App Check를 적용해 인증된 앱에서만 API를 호출할 수 있도록 제한하라.
- Google AI Studio 또는 GCP 콘솔에서 프로젝트 소비 한도(project spend caps)를 설정하고, Pub/Sub + Cloud Function 조합으로 한도 초과 시 billing 계정을 자동으로 분리하는 파이프라인을 구축해 두어야 한다. 단순 이메일 알림은 수 시간 지연이 있어 실질적인 방어가 되지 않는다.
- 기존에 Firebase Authentication 등 다른 용도로 만든 프로젝트에 나중에 Gemini API를 추가하는 경우, 해당 프로젝트의 기존 API 키들이 모두 Gemini 접근 권한을 갖게 된다. 반드시 기존 키 목록을 전수 점검하고 불필요한 API 권한을 제거해야 한다.
Code Example
snippet
# GCP billing 계정을 프로그래밍 방식으로 분리하는 비상 방법 (공식 문서 기반)
# 주의: 프로덕션 앱에는 사용 금지. 연결된 리소스가 삭제될 수 있음.
# gcloud CLI로 billing 계정 분리
gcloud billing projects unlink PROJECT_ID
# 또는 Pub/Sub + Cloud Function 조합으로 예산 초과 시 자동 분리
# 1. GCP 콘솔 > Billing > Budgets & alerts > Create budget
# 2. Alert threshold 설정 (예: 80%)
# 3. 'Connect a Pub/Sub topic' 선택
# 4. 아래와 같은 Cloud Function 배포
import base64
import json
from googleapiclient import discovery
def stop_billing(data, context):
pubsub_data = base64.b64decode(data['data']).decode('utf-8')
pubsub_json = json.loads(pubsub_data)
cost_amount = pubsub_json['costAmount']
budget_amount = pubsub_json['budgetAmount']
project_name = pubsub_json['budgetDisplayName'] # 프로젝트명을 예산 이름으로 사용
if cost_amount >= budget_amount:
billing = discovery.build('cloudbilling', 'v1')
# billing 계정 분리
billing.projects().updateBillingInfo(
name=f'projects/{PROJECT_ID}',
body={'billingAccountName': ''}
).execute()
print(f'Billing disabled for project {PROJECT_ID}')
# API 키 제한 설정 (Google Cloud Console > APIs & Services > Credentials)
# - Application restrictions: HTTP referrers (web sites) -> 자신의 도메인만 허용
# - API restrictions: Restrict key -> generativelanguage.googleapis.com 만 선택Terminology
Firebase AI LogicFirebase 프로젝트에서 Gemini API 등 Google의 AI 기능을 쉽게 연동할 수 있게 해주는 Firebase 서비스. 활성화하면 기존 Firebase 키로도 Gemini 요청이 가능해진다.
unrestricted API key어떤 API든, 어떤 도메인에서든 제한 없이 사용 가능한 상태의 API 키. Google Cloud에서 키를 처음 만들면 기본값이 이 상태인 경우가 많다.
App CheckFirebase에서 제공하는 보안 기능으로, 실제로 인증된 앱(iOS/Android/Web)에서만 Firebase 리소스에 접근할 수 있도록 검증하는 장치. 봇이나 비정상 클라이언트를 차단하는 데 도움된다.
spend capAPI 사용 비용이 특정 금액을 초과하면 자동으로 서비스를 차단하는 하드 한도. 단순 알림과 달리 실제로 요청을 막는다.
Pub/SubGoogle Cloud의 메시지 큐 서비스. 예산 초과 같은 이벤트 발생 시 메시지를 발행(publish)하면, 이를 구독(subscribe)하는 Cloud Function 등이 자동으로 대응 조치를 취하는 자동화 파이프라인 구축에 사용된다.
billing aggregation delay클라우드 서비스에서 실제 API 호출 비용이 청구 시스템에 반영되기까지 걸리는 지연 시간. Google의 경우 최대 수 시간이 걸릴 수 있어, 예산 알림이 늦게 오는 근본 원인이 된다.