기능은 완성됐는데 보안 설계가 없다
AI로 SaaS를 빠르게 만들 수 있게 됐다. 문제는 AI가 비즈니스 로직은 잘 구현하지만, 해커가 그 비즈니스 로직을 어떻게 우회하려 할지는 잘 모른다는 거다.
바이브코딩으로 만든 SaaS에서 반복적으로 발견하는 공격 패턴 세 가지가 있다.
패턴 1: 인증 없는 API 엔드포인트
AI가 API를 생성할 때 일부 엔드포인트에 인증을 빠뜨리는 경우가 있다. 공격자는 배포된 앱의 JS 번들을 분석하거나, Burp Suite로 트래픽을 분석해서 모든 API 경로를 찾아낸다.
# 공격자 관점의 API 열거
curl -s https://yourapp.com/api/users # 인증 없이 접근 가능한 경우
curl -s https://yourapp.com/api/admin/stats
curl -s https://yourapp.com/api/export/users.csv
방어: 모든 API에 기본값으로 인증 미들웨어를 적용하고, 필요한 경우에만 public으로 명시적으로 열어두는 "deny by default" 정책을 채택한다.
패턴 2: 구독 플랜 우회 (IDOR + 기능 잠금 우회)
Free 플랜 사용자가 Pro 플랜 기능에 접근을 시도한다. 단순히 UI에서 버튼을 숨기는 것으로는 막을 수 없다. API 레벨에서 플랜을 검증해야 한다.
# 위험: UI에서만 제한
# Pro 버튼을 숨겨도 API 직접 호출은 가능
# 안전: API 레벨에서 검증
@require_pro_plan
def export_data(request):
user_plan = request.user.subscription.plan
if user_plan != 'pro':
return JsonResponse({'error': 'Pro plan required'}, status=403)
# ... 로직
패턴 3: 다른 사용자 데이터 접근 (IDOR)
IDOR(Insecure Direct Object Reference)는 다른 사용자의 ID를 URL에 넣어서 남의 데이터에 접근하는 공격이다.
# 공격자가 본인 주문 ID를 확인한 뒤 다른 ID를 시도
GET /api/orders/1001 # 본인 주문
GET /api/orders/1000 # 다른 사용자 주문? 접근 가능한지 확인
GET /api/orders/1002
# 위험
def get_order(request, order_id):
order = Order.objects.get(id=order_id) # 누구의 주문인지 검증 없음
return JsonResponse(order.to_dict())
# 안전
def get_order(request, order_id):
order = Order.objects.get(id=order_id, user=request.user) # 본인 주문만
return JsonResponse(order.to_dict())
SaaS 보안, 어디서부터 시작할까
위 세 가지를 막으려면 결국 "내 코드가 인증/인가를 제대로 하고 있는가"를 전수 검토해야 한다. AI에게 "현재 API 엔드포인트 목록을 정리하고, 각각 인증이 필요한지 검토해줘"라고 요청하는 것도 좋은 출발점이다.
인프라/설정 레벨의 문제는 CodeScan으로 먼저 확인하고, 코드 레벨은 직접 또는 AI 보조로 검토하는 조합이 효율적이다.