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은 환불을 거부했다.
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
# 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
관련 논문
1-bit/Ternary Bonsai Image 4B: 로컬 디바이스용 이미지 생성 모델
4B 파라미터 이미지 생성 모델의 가중치를 1비트/3값으로 극단적으로 압축해서 iPhone에서도 돌아가게 만든 모델. 7.75GB짜리 diffusion transformer를 0.93GB까지 줄였다.
Tiny-vLLM: C++와 CUDA로 만드는 고성능 LLM 추론 엔진
vLLM의 핵심 기능을 C++와 CUDA로 직접 구현하며 배울 수 있는 교육용 LLM 추론 엔진 프로젝트로, 소스코드와 단계별 강의가 함께 제공된다.
일반 데이터센터 GPU에서 요청당 3,000 tokens/s 실시간 LLM 추론
Kog AI가 8× AMD MI300X에서 요청당 3,000 tokens/s를 달성하는 LLM 추론 엔진을 공개했고, 기존 소프트웨어 스택의 병목을 GPU 메모리 대역폭 최대화로 풀어냈다는 내용이다.
LLM을 위한 수면과 유사한 컨텍스트 통합 메커니즘
LLM이 긴 컨텍스트를 처리할 때 발생하는 Attention 비용 문제를 해결하기 위해, 사람의 수면처럼 주기적으로 컨텍스트를 fast weight에 압축·저장하는 새로운 메커니즘을 제안한 논문이다.
CODA: Transformer 블록을 GEMM-Epilogue 프로그램으로 재작성하기
GPU에서 Transformer 학습 시 발생하는 메모리 병목을 해결하기 위해, 정규화·활성화 등 소규모 연산들을 GEMM 출력이 칩 위에 있는 동안 함께 실행하는 커널 추상화 CODA를 소개한다. LLM이 이 추상화를 활용해 고성능 커널을 자동 생성할 수 있다는 점이 특히 주목받고 있다.
KV-Fold: 긴 컨텍스트 추론을 위한 One-Step KV-Cache Recurrence
모델 수정 없이 KV 캐시를 청크 간 누산기로 쓰면 128K 토큰까지 100% 정확도로 정보를 검색할 수 있다.
Swift로 LLM 학습시키기 Part 1: 행렬 곱셈을 Gflop/s에서 Tflop/s로 끌어올리기