본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 28. 13:40

실제 운영 중인 Lovable 앱의 보안 문제를 스캔하며 발견한 항상 반복되는 5가지 문제점

요약

Lovable과 Supabase를 사용하여 구축된 AI 생성 앱에서 반복적으로 발생하는 5가지 보안 취약점을 분석합니다. 특히 RLS(Row-Level Security) 미설정으로 인한 데이터 노출과 Stripe 웹훅 검증 누락 문제를 중점적으로 다룹니다.

핵심 포인트

  • Supabase 사용 시 RLS(행 수준 보안) 활성화 필수
  • anon key를 통한 인증되지 않은 데이터 접근 위험성
  • Stripe 웹훅 처리 시 서명(Signature) 검증 필수
  • AI 생성 코드는 운영 환경 보안 강화가 필요함

Martin Fowler가 오늘 제가 몇 달 동안 지켜봐 온 결과물을 발표했습니다.

그의 글 제목은 "The VibeSec Reckoning"입니다. 핵심 논지는 다음과 같습니다: AI 에이전트에게 안전하게 행동하라고 말하는 것은 그것이 안전하도록 강제하는 것과 같지 않다는 것입니다. 2026년 1분기 연구 결과에 따르면, AI로 구축된 앱의 91.5%가 최소 하나 이상의 치명적인 취약점(critical vulnerability)을 가지고 있습니다.

저는 약 3주 전부터 Lovable + Supabase 앱을 구체적으로 연구하기 시작했습니다. 제가 발견한 내용은 놀랍지 않았지만, 그 일관성은 놀라웠습니다.

제가 살펴보는 거의 모든 앱에서 동일한 다섯 가지 문제가 나타납니다. 이는 개발자들이 부주의해서가 아닙니다. 도구들이 데모에서는 작동하지만 운영 환경의 보안 강화(production hardening)는 개발자에게 맡기는 코드를 기본값으로 생성하기 때문이며, 대부분의 Lovable 창업자들은 비기술적(non-technical)이기 때문입니다.

제가 계속해서 발견하고 있는 내용들을 소개합니다.

이 문제는 가장 비용이 많이 드는 문제입니다.

Lovable은 모든 앱을 anon key를 사용하여 Supabase에 연결합니다. anon key는 의도적으로 공개되어 있습니다. Supabase가 그렇게 설계했기 때문입니다. 보안 모델은 사용자가 모든 테이블에 행 수준 보안 (Row-Level Security, RLS) 정책을 활성화했다고 가정합니다.

하지만 Lovable은 이를 활성화하지 않습니다. 모든 새로운 Postgres 테이블에서 RLS는 기본적으로 꺼져(off) 있습니다.

공격 방법은 매우 간단합니다:

curl -X GET \
"https://YOUR_PROJECT.supabase.co/rest/v1/users?select=*" \
-H "apikey: YOUR_ANON_KEY"

이렇게 하면 인증되지 않은 상태로, 로그인 없이도 사용자 테이블의 모든 행이 반환됩니다.

CVE-2025-48757은 170개 이상의 실제 운영 중인 Lovable 앱에서 이와 정확히 일치하는 패턴이 발생했음을 기록했습니다. CVSS 심각도(severity): 9.3 Critical.

확인 방법: Supabase 대시보드를 여세요. Database → Tables로 이동합니다. RLS 열을 확인하세요. 개인 데이터가 포함된 테이블 중 하나라도 "Disabled"라고 표시되어 있다면 취약한 상태입니다.

전체 수정 가이드: Lovable 앱을 위한 Supabase RLS

만약 귀하의 앱이 결제를 처리한다면, Stripe는 결제가 성공했을 때 귀하의 웹훅(webhook) 엔드포인트로 이벤트를 전송합니다. 귀하의 핸들러(handler)는 각 이벤트가 실제로 Stripe에서 온 것인지 확인해야 합니다.

대부분의 AI 생성 웹훅 핸들러는 이 과정을 생략합니다. 그들은 JSON을 파싱하고 그것을 그대로 신뢰합니다.

// Lovable이 일반적으로 생성하는 코드:
const event = JSON.parse(req.body)
if (event.type === 'checkout.session.completed') {
await fulfillOrder(event.data.object) // 검증 없음
}
이 코드를 사용하면, 웹훅(Webhook) URL을 찾아낸 누구나 가짜 'payment.succeeded' 이벤트를 POST로 보내어 무료로 주문 이행 로직을 실행시킬 수 있습니다.

확인 방법: 코드베이스에서 constructEvent를 검색하세요. 이 함수가 없다면 서명(Signatures) 검증이 이루어지지 않고 있는 것입니다.

전체 해결 가이드: Lovable 앱을 위한 Stripe 웹훅 보안 (Stripe Webhook Security for Lovable Apps)

배포된 Lovable 앱 중 아무 곳이나 마우스 오른쪽 버튼으로 클릭하세요. '페이지 소스 보기(View Page Source)'를 선택합니다. Cmd+F를 누르고 sk_를 검색하세요.

놀랍게도 상당수의 앱이 실제 키 값을 반환합니다. Stripe 비밀 키(Secret keys), OpenAI 키, Supabase 서비스 역할 키(service_role keys) 등이 모든 방문자에게 노출되는 JavaScript 번들(Bundle) 안에 그대로 놓여 있습니다.

AI 빌더들은 기능적인(Functional) 코드를 작성합니다. 기능적이라는 것은 API 호출이 작동한다는 의미입니다. 아무도 그들에게 키를 서버 측(Server-side)으로 옮기라고 프롬프트(Prompt)를 주지 않습니다. 결과적으로 키는 React 컴포넌트에 포함되고, 번들로 빌드되어 모든 브라우저로 전송됩니다.

피해 규모: 키를 찾아낸 누구나 이를 사용할 수 있습니다. Stripe 비밀 키는 고객 정보를 수정하거나 환불을 실행할 수 있습니다. OpenAI 키는 귀하의 계정으로 비용을 청구합니다. Supabase service_role 키는 전체 데이터베이스에 대한 완전한 관리자 권한(Admin access)을 가집니다.

확인 방법: 배포된 앱의 소스 보기를 실행하세요. sk_, sk-, service_role을 검색하세요. 만약 이 중 하나라도 실제 키 문자열(단순 변수명이 아닌)을 반환한다면, 데이터 유출(Leak)이 발생한 것입니다. 코드를 수정하기 전에 즉시 키를 교체(Rotate)하십시오.

전체 해결 가이드: Lovable 앱을 위한 비밀 정보 관리 (Secrets Management for Lovable Apps)

대부분의 Lovable 앱에는 채팅, 생성, 요약 또는 이와 유사하게 AI API를 호출하는 기능이 최소 하나 이상 포함되어 있습니다. 이러한 기능을 구동하는 엔드포인트(Endpoints)에는 속도 제한(Rate limiting)이 거의 설정되어 있지 않습니다.

IP당 스로틀링(Throttle)도 없고, 사용자당 제한(Per-user cap)도 없으며, 전역 예산 제한(Global budget limit)도 없습니다.

공격 방식: 한 명의 사용자가 엔드포인트를 찾아내 밤새도록 요청을 루프(Loop)로 돌립니다. 아침이 되면, 사용된 모델에 따라 OpenAI 청구 금액이 1,000달러에서 5,000달러에 달할 수 있습니다. 결제는 이미 완료된 상태일 것입니다.

while true; do  
curl -X POST https://yourapp.com/api/chat \  
-d '{"prompt": "..."}'  
done  

스크립트 하나로. 하룻밤 사이에. 끝납니다.

확인 방법: AI 엔드포인트 (endpoint)에 50번의 요청을 연속해서 빠르게 보냅니다. 만약 모든 요청이 속도 제한 (throttling) 없이 200 OK를 반환한다면, 속도 제한 (rate limiting)이 설정되어 있지 않은 것입니다.

전체 해결 가이드: Lovable 앱을 위한 속도 제한 (Rate Limiting)

Lovable은 클라이언트 측 검증 (client-side validation) 기능인 필수 속성 (required attributes), 정규 표현식 패턴 (regex patterns), 에러 메시지 등을 포함한 양식 (forms)을 생성합니다. 이는 사용자 경험 (UX)을 위한 것이지, 보안을 위한 것이 아닙니다.

서버 측 핸들러 (server-side handler)는 일반적으로 도착하는 모든 JSON을 신뢰합니다. curl 요청은 양식을 완전히 우회합니다.

curl -X POST https://yourapp.com/api/submit \  
-H "Content-Type: application/json" \  
-d '{"email": "alert(1)", "amount": -9999}'

만약 서버가 이를 수락하고 데이터베이스 (database)에 기록한다면, 문제가 있는 것입니다.

확인 방법: 데이터베이스에 기록을 수행하는 모든 엔드포인트 (endpoint)를 찾습니다. 양식의 클라이언트 측 규칙을 통과하지 못하는 페이로드 (payload)를 curl을 통해 직접 전송합니다. 서버가 이를 수락한다면, 검증 (validation)이 클라이언트 측에서만 이루어지고 있는 것입니다.

이 다섯 가지 문제는 버그가 아닙니다. AI가 실수를 한 것도 아닙니다. AI는 당신이 요청한 것을 정확히 생성했을 뿐이며, 당신이 요청한 것은 바로 "작동하는 앱"이었습니다. 프로덕션 강화 (production hardening) (인증 강제 (authentication enforcement), 입력 검증 (input validation), 속도 제한 (rate limiting), 웹훅 보안 (webhook security))를 위해서는 다른 방식의 요청이 필요합니다.

대부분의 Lovable 창업자들은 비기술자입니다. 그들은 이러한 것들을 요청해야 한다는 사실을 모릅니다. 그리고 무료 자동 스캐너 (automated scanners)가 이 모든 것을 찾아내지는 못합니다. RLS (Row Level Security) 예외 사례 (edge cases), 비즈니스 로직 취약점 (business logic exploits), 그리고 웹훅 검증 (webhook verification)은 수동 검증 (manual verification)이 필요합니다.

대부분의 Lovable 창업자들은 비기술자입니다. 그들은 이러한 것들을 요청해야 한다는 사실을 모릅니다. 그리고 무료 자동 스캐너 (automated scanners)가 이 모든 것을 찾아내지는 못합니다 — RLS 예외 사례 (edge cases), 비즈니스 로직 취약점 (business logic exploits), 그리고 웹훅 검증 (webhook verification)은 수동 검증 (manual verification)이 필요합니다.

저는 무엇을 살펴봐야 하는지, 그리고 통과/실패 기준이 무엇인지에 대해 쉬운 영어로 각 항목을 설명하는 5분 내외의 무료 자가 점검 도구를 만들었습니다: rivetzco.com/self-check

이메일 등록은 필요하지 않습니다. 만약 문제를 발견하신다면, 위의 가이드들에 단계별 해결 방법이 안내되어 있습니다.

만약 누군가가 더 깊이 있게 파고들어 주기를 원하신다면 — 해결 방법이 올바른지 검증하고, 자동화 도구가 놓치는 예외 케이스 (edge cases)를 확인하며, 실제로 변경 사항을 구현하는 작업까지 — 그것이 바로 Rivetz가 하는 일입니다. 고정 가격으로, 비동기 (async) 방식으로만 진행됩니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0