AI 코딩 도구의 보급으로 웹 서비스를 만드는 것은 쉬워졌지만, 보안 사고도 그만큼 늘어났습니다. 이 글에서는 바이브코딩 환경에서 실제로 발생한 보안 사고 사례들을 정리했습니다. 가명 처리 또는 익명화된 사례들이지만, 모두 실제 상황을 기반으로 합니다.
같은 실수를 반복하지 않기 위해, 불편하더라도 직접적으로 기술합니다.
사례 1 — Supabase 서비스 키 GitHub 노출로 전체 DB 삭제
상황
스타트업 A사는 Bolt.new를 이용해 SaaS 서비스를 만들었습니다. 개발 과정에서 Supabase를 데이터베이스로 연동했고, supabase.ts 파일에 service_role 키를 직접 입력했습니다. 코드를 GitHub에 올리면서 이 파일이 포함됐고, 리포지토리는 public으로 설정되어 있었습니다.
피해
- GitHub 업로드 후 약 4시간 만에 해커가 키를 수집
- Supabase API를 통해 전체 테이블 데이터 다운로드
- 데이터 다운로드 후 모든 테이블 삭제
- 사용자 2,000여 명의 이메일, 결제 정보 유출
- 서비스 운영 중단 3일, 복구 비용 수백만 원
원인 분석
Supabase의 anon key는 공개해도 괜찮지만, service_role key는 RLS(Row Level Security)를 무시하고 모든 데이터에 접근할 수 있는 관리자 권한입니다. Bolt가 생성한 코드에 두 키의 차이가 명확히 설명되지 않았고, 개발자는 이를 구분하지 못한 채 service_role 키를 클라이언트 코드에 넣었습니다.
교훈
- GitHub에 올리기 전
git-secrets또는 GitHub Secret Scanning 활성화 - Supabase service_role 키는 절대 프론트엔드 코드에 넣지 말 것
- 리포지토리를 public으로 만들기 전 전체 파일 검토 필수
사례 2 — Replit 환경변수 설정 오류로 OpenAI 키 노출
상황
프리랜서 개발자 B씨는 Replit으로 ChatGPT API를 사용하는 챗봇 서비스를 만들었습니다. Replit의 Secrets 기능을 사용했지만, 실수로 환경변수 대신 코드에 직접 키를 입력한 버전을 배포했습니다. Replit 프로젝트는 "Share" 기능으로 공유 링크를 만든 상태였습니다.
피해
- 공유 링크를 통해 소스 코드가 노출됨
- OpenAI API 키 탈취
- 해커가 해당 키로 대량 API 호출 → 하루 만에 $3,400 청구
- OpenAI 측에 신고했으나 일부 금액만 환불 처리
Replit 특유의 위험
Replit은 교육용으로 설계된 플랫폼이라 코드 공유가 기본 기능입니다. "Share" 버튼을 누르면 다른 사람이 소스 코드 전체를 볼 수 있습니다. 바이브코딩 입문자들이 이 점을 간과하는 경우가 많습니다.
사례 3 — CORS 전체 허용 + 인증 없는 API로 개인정보 수집
상황
스타트업 C사는 Cursor를 이용해 React + FastAPI 구조의 서비스를 만들었습니다. 개발 초기 CORS 에러를 빠르게 해결하기 위해 allow_origins=["*"]를 설정했고, 이 설정이 프로덕션에 그대로 배포됐습니다. 또한 /api/users/list 엔드포인트가 인증 없이 전체 사용자 목록을 반환하고 있었습니다.
피해
- 경쟁사 또는 악의적 제3자가 API를 크롤링해 전체 회원 데이터 수집
- 수집된 이메일로 스팸 메일 발송
- 개인정보보호법 위반으로 과태료 처분 가능성
CORS 오해
많은 개발자들이 CORS를 "내 사이트를 보호하는 것"으로 오해합니다. CORS는 브라우저 정책으로, 브라우저를 통하지 않는 서버 간 요청이나 curl 요청에는 적용되지 않습니다. 즉, CORS를 전체 허용해도 서버를 직접 공격하는 것은 막을 수 없습니다.
사례 4 — 파일 업로드 취약점으로 웹쉘 삽입
상황
D사는 AI 도구로 사용자 파일 업로드 기능을 구현했습니다. AI가 생성한 코드는 파일 업로드 기능은 있었지만, 파일 타입 검증이 없었습니다. 공격자는 shell.php라는 파일을 이미지 파일처럼 위장해 서버에 업로드했습니다.
피해
- 웹쉘을 통해 서버 원격 명령 실행
- 서버 내 모든 파일 접근 가능
- 데이터베이스 접속 정보 탈취
- 서버가 DDoS 공격의 경유지로 악용
파일 업로드 보안 필수 사항
# 위험: 확장자 검증 없음
def upload_file(request):
file = request.FILES['file']
file.save(settings.MEDIA_ROOT + file.name) # 위험!
# 안전: 화이트리스트 기반 검증
ALLOWED_EXTENSIONS = {'jpg', 'jpeg', 'png', 'gif', 'pdf'}
def upload_file(request):
file = request.FILES['file']
ext = file.name.split('.')[-1].lower()
if ext not in ALLOWED_EXTENSIONS:
raise ValueError("허용되지 않는 파일 형식")
# 추가로 파일 내용(magic bytes)도 검증 필요
사례 5 — DEBUG 모드 배포로 DB 구조 노출
상황
E사는 Django 앱을 급하게 배포하면서 DEBUG=True 상태로 운영했습니다. 사이트에서 에러가 발생할 때마다 Django의 상세 디버그 페이지가 사용자에게 노출됐습니다. 해커는 의도적으로 에러를 유발해 데이터베이스 구조, 환경변수 이름, 서버 경로 등 민감한 정보를 수집했습니다.
결과적 피해
수집된 정보를 바탕으로 정교한 SQL 인젝션 공격이 이루어졌습니다. 에러 페이지 하나로 시작된 정보 수집이 결국 데이터베이스 탈취로 이어진 사례입니다.
공통 패턴 분석
5가지 사례를 분석하면 공통된 패턴이 보입니다:
- AI가 생성한 코드를 그대로 배포 (보안 검토 없음)
- 개발 편의를 위한 설정이 프로덕션에 그대로 반영
- 배포 전 보안 점검 절차 부재
- "어차피 작은 사이트라서 괜찮겠지"라는 안일함
규모가 작다고 해서 해커가 노리지 않는 것이 아닙니다. 자동화된 스캐너들은 하루에도 수백만 개의 사이트를 스캔합니다. 기본 보안이 갖춰지지 않은 사이트는 자동으로 공격 대상이 됩니다.
내 사이트도 위 사례들처럼 취약한지 확인하세요 → CodeScan 무료 스캔