핀수로그
  • AI API Key 탈취 그리고 환불여정: 소 잃고 뇌 약간 고치기
    2026년 06월 21일 16시 45분 33초에 업로드 된 글입니다.
    작성자: 핀수
    728x90
    반응형

    어머 저거 뭐야?

     
    아침에 자고 일어났는데 구글 클라우드 코리아에서 10만원이 결제되었다.

     
    ???
     
    몇 달전 출시했던 앱에 Gemini API 요청을 통해 레시피를 생성해주는 기능이 있긴한데...
    하지만 그건 요청 한건 당 100원도 하지 않는 금액이라 10만원이 나올 수 없었다.
    그리고 광고도 달아놔서 10만원이 나오려면..광고비는 그것보다 더 나왔겠지
     
    그래서 애드몹에 들어가봤는데 데이터가 없다.

     

    비정상적인 접근이 의심되어 AI 대시보드에 들어가보니.. 맞는 것 같았다.
    내 앱이 사용중인 모델은 하나뿐인데 여러 모델이 하루만에 사용량이 치솟은 것이다.
     
    뭔가 잘못됐음을 깨닫고 환불을 받을 수 있는 방법을 찾아보았다.

     
    구글 클라우드로 들어가서 '결제 관련 지원 받기' 를 누르면 채팅창이 열린다.
    이때 내 상황을 설명하면 AI가 판단해서 실제 상담원을 연결해준다.
     

     
    열심히 상황을 설명했다. (영어로 진행됨)
    1회성 면제 조항이 있다고 하더니 정말인듯 하다.
    다음부터 생기는 문제는 내 책임이라니 너무 무섭다..진짜 정신 똑바로 차리자
     
    아무튼 다행히 환불해준다고 했다.
    과금이 20만원 정도 되었고 실제로 결제 된 건 10만원..
    모두 환불처리 되겠지? 잠자코 기다리는 중이다.
     
     

    원인은 무엇인가

    GPT와 논의 끝에 가장 유력한 노출 경로는 앱 패키지 자체라는 결론을 내렸다.
    나는 중요한 정보는 .env 파일에 관리를 하고 있었고,
    flutter 프로젝트 내에서 .env 파일에 접근하기 위해 pubspec.yaml 에 assets으로 등록되어 있었다.
    (flutter_dotenv 공식 사용법이라고 한다.)
     
    Flutter 프로젝트 내에서 .env 파일이 pubspec.yaml의 assets로 등록되면, 빌드된 앱 패키지에 포함될 수 있다.
    실제 빌드 기록에서도 iOS release 산출물 경로에
    App.framework/flutter_assets/.env가 출력 대상으로 잡혀 있었다.
     
    깃헙에 안올라 갔어도, 앱스토어/테스트플라이트/로컬 배포된 앱을 분석하면 Gemini 키를 뽑을 수 있는 상태였던 것이다.

     

    소 잃고 뇌 약간 고치기

    그럼 이제 어떻게 해야할까?
    먼저, 탈취된 것으로 의심되는 키를 제거 또는 변경한다.
    그리고 구글 AI 대시보드에서 사용 한도 알림, 지출 한도를 걸어두어야 한다.
    한도 알림은 걸어놨었는데, 지출 한도는 깜빡하고 안걸었다... 당장 걸어둠
     
    이런 경우를 방지하기 위해 API 키를 어떻게 숨길까를 고민하는 것보다
    호출 권한을 옮기는 게 중요하다.
    서버 측 통제점을 두어 앞단에 두는 것이 아니라 경계 뒤로 옮겨 이를 사전에 방지하는 것이다.
     
    근데 내 앱은 워낙 소규모라 백엔드가 따로 존재하지 않는다.
    이때 두가지 대안이 있는데,
     
    첫번째는 Firebase AI Logic + App Check를 사용하는 것이다.
    기존 방식 .env 내부의 키를 가지고 AI를 직접 호출하는 구조에서
    앱에서 Firebase AI Logic 프록시 + App Check 를 통해 AI를 호출하는 구조이다.
     
    두번째는 Firebase Cloud Functions 를 이용해 작은 백엔드를 하나 만드는 방식이다.
    서버를 빌리고 관리하는 것이 아니라 함수 하나만 배포하는 것이다.
    앱에서 Cloud Function을 호출하고, 함수 안에서 AI 키를 사용해 호출하는 구조이다.
     
    결론은..
    Gemini API 키를 앱에 직접 포함하는 구조를 제거하고, Firebase AI Logic + App Check로 호출 경로를 옮겼다.
    이를 통해 앱에는 Gemini 키를 넣지 않고,
    Firebase 프로젝트와 App Check를 통해 내가 배포한 앱에서 온 요청인지 검증할 수 있게 했다.
     
    Cloud Functions를 사용하는 방법도 있었지만,
    이 프로젝트는 별도 백엔드가 없는 Flutter 앱이었기 때문에 사고 대응 관점에서는 구현 범위가 컸다.
    이번에는 사용자별 rate limit이나 정교한 정책 구현보다, 노출된 키를 빠르게 제거하고 추가 피해를 줄이는 것이 우선이었다.
     
    따라서 Firebase AI Logic + App Check를 1차 복구책으로 선택했고,
    추후 더 세밀한 제어가 필요해지면 Cloud Functions나 별도 백엔드로 확장할 수 있다고 판단했다.
     
     

    또 하나를 배웠다

    이번 일을 통해 모바일 앱에서 .env는 서버의 .env처럼 안전한 보관소가 아니라는 점을 배웠다.
    문제의 핵심은 파일 형식이 아니라, 비용과 권한을 가진 API 키가 클라이언트 앱 안에 있었다는 점이었다.
     
    그래서 Gemini 직접 호출을 제거하고 Firebase AI Logic + App Check로 호출 경로를 옮겼다.
    앞으로는 기능 구현뿐 아니라 '권한은 어디에 있어야 안전한가'를 먼저 생각하게 될 것 같다.

    728x90
    반응형
    댓글