# Task-2125 Phase 3: Devil's Advocate 검토 (Loki)

**날짜**: 2026-04-23
**역할**: 보안팀(레드팀) 팀장 — 비판적 검증
**대상**: Phase 2 E2E 검증 결과

---

## 1. 검증 신뢰도 평가: LOW

Phase 2 검증의 전체 신뢰도를 **LOW**로 판정한다. 근거는 아래와 같다.

---

## 2. 핵심 발견: Supabase 프로젝트 ID 불일치 (Phase 2가 놓친 것)

**Phase 2의 가장 큰 실수**: Edge Functions 404를 단순히 "미배포"로 결론지었으나, 근본 원인은 **Supabase 프로젝트 ID 불일치**일 가능성이 높다.

| 소스 | 프로젝트 ID | 비고 |
|------|------------|------|
| `.env` / `.env.production` | `zayhfjuwviporbzokudr` | 프론트엔드가 호출하는 대상 |
| `supabase/config.toml` | `dmyjpvxhlwmrqqqisbjj` | CLI 배포 대상 (supabase functions deploy) |
| `docs/d4-production-checklist.md` | `dmyjpvxhlwmrqqqisbjj` | 프로덕션 문서 기준 |

**해석**:
- `supabase functions deploy`를 실행하면 config.toml의 `dmyjpvxhlwmrqqqisbjj` 프로젝트에 배포됨
- 프론트엔드는 `.env.production`의 `zayhfjuwviporbzokudr.supabase.co`로 호출함
- **즉, Edge Functions가 다른 프로젝트에 배포되어 있을 수 있으며, Phase 2가 테스트한 URL에서만 404인 것일 수 있음**
- 또는 양쪽 모두 미배포일 수 있으나, Phase 2는 이 가능성을 전혀 검토하지 않았음

**Phase 2의 결론 "미배포"는 불완전**: 정확한 표현은 "zayhfjuwviporbzokudr 프로젝트에 미배포 (dmyjpvxhlwmrqqqisbjj 미확인)"이어야 한다.

---

## 3. 놓친 위험 요소

### 3-1. Edge Function 검증 커버리지 부족
- 코드베이스에 Edge Functions가 **15개** 존재하나, Phase 2는 **5개**만 테스트
- 미검증 10개: `analyze-customer`, `analyze-performance`, `crawl-metrics`, `create-test-user`, `evaluate-consultation`, `get-vapid-key`, `parse-premium-file`, `posthog-webhook`, `send-push`, `transcribe-call`
- 이 중 CRM 관련(`analyze-customer`, `evaluate-consultation`, `transcribe-call`)과 푸시 알림(`send-push`, `get-vapid-key`)은 비즈니스 크리티컬 기능임

### 3-2. "코드 구현 완료 = 실제 동작" 착각
- Phase 2에서 "코드 구현 ✅"로 표기된 12개 기능 중, 실제 동작 확인된 것은 **0개**
- 런타임 에러 가능성: Edge Functions이 Deno.env.get()으로 읽는 환경 변수(`GEMINI_API_KEY`, `ANTHROPIC_API_KEY` 등)가 Supabase Secrets에 설정되어 있는지 미확인
- `generate-content` 함수가 `claude-sonnet-4-6` 모델을 참조하는데, Anthropic API 키가 Supabase Secrets에 설정되지 않았다면 배포 후에도 500 에러 발생

### 3-3. Google OAuth 제한의 영향 과소평가
- 인증 없이 검증 가능한 것은 로그인 페이지 UI와 리다이렉트뿐
- **전체 사용자 플로우(생성 → 저장 → 공유)가 단 한 번도 E2E로 검증되지 않음**
- 이것은 "E2E 검증"이라 부를 수 없으며, "API 가용성 점검 + 코드 정적 분석"에 불과

---

## 4. 보안 관점 이슈

### 4-1. [CRITICAL] service_role 키 평문 노출
- `.env` 파일에 `INSURO_NEW_SERVICE_ROLE_KEY`가 평문으로 존재
- service_role 키는 RLS(Row Level Security) 우회 가능 — **DB 전체 읽기/쓰기 가능**
- 이 키의 JWT payload에서 ref(`zayhfjuwviporbzokudr`)와 role(`service_role`) 확인 가능
- `.env` 파일이 git에 커밋되어 있다면 즉시 키 로테이션 필요

### 4-2. [HIGH] anon key 공개 노출
- `.env.production`에 `VITE_SUPABASE_PUBLISHABLE_KEY`가 포함됨
- Vite의 `VITE_` 접두어는 클라이언트 번들에 포함됨 — 브라우저 소스에서 추출 가능
- anon key 자체는 설계상 공개 가능하나, RLS 정책이 미흡하면 데이터 유출 위험

### 4-3. [HIGH] 모든 Edge Functions에 verify_jwt = false
- `supabase/config.toml`에서 **10개 함수 전부** `verify_jwt = false`로 설정
- Supabase Gateway 레벨의 JWT 검증이 비활성화됨
- 함수 내부에서 수동으로 JWT를 검증하는 코드가 일부 있으나(`premium-chat`, `newsletter-chat`), 나머지는 미확인
- `create-test-user`가 JWT 없이 호출 가능하면 임의 사용자 생성 취약점

### 4-4. [MEDIUM] CORS 와일드카드 (*)
- 모든 Edge Functions에서 `Access-Control-Allow-Origin: "*"` 설정
- 프로덕션에서는 `insuro.biz` 도메인으로 제한해야 함
- 현재 설정은 CSRF 공격 벡터를 열어놓은 상태

### 4-5. [MEDIUM] API 키 다수 평문 노출
- `.env` 파일에 네이버 API 키, 네이버 검색광고 API 키, VAPID 비공개 키, PostHog API 키 등 **8개 이상의 시크릿**이 평문 저장
- `NAVER_SEARCHAD_API_KEY`, `NAVER_SEARCHAD_SECRET_KEY`, `NAVER_SEARCHAD_CUSTOMER_ID` — 검색광고 계정 탈취 시 과금 피해 가능

---

## 5. Edge Functions 404 원인 추정

| 가설 | 가능성 | 근거 |
|------|--------|------|
| A. zayhfjuwviporbzokudr에 한 번도 배포 안 됨 | **높음** | config.toml이 다른 프로젝트를 가리킴 |
| B. dmyjpvxhlwmrqqqisbjj에만 배포됨 | **중간** | config.toml 기준 배포 대상이 이쪽 |
| C. 양쪽 모두 미배포 | **중간** | supabase CLI로 배포한 기록 미확인 |
| D. 배포 후 삭제/비활성화됨 | **낮음** | 의도적 삭제 근거 없음 |

**가장 유력한 시나리오**: config.toml의 project_id가 구 프로젝트(`dmyjpvxhlwmrqqqisbjj`)를 가리키고 있어, `supabase functions deploy`를 실행해도 프론트엔드가 호출하는 `zayhfjuwviporbzokudr`에는 배포되지 않는 구조적 문제.

**조치**: 다음 명령으로 즉시 확인 가능
```bash
# config.toml 프로젝트 ID 수정 후 배포
supabase functions list --project-ref zayhfjuwviporbzokudr
supabase functions list --project-ref dmyjpvxhlwmrqqqisbjj
```

---

## 6. 개선 제안

### 즉시 (P0)
1. **Supabase 프로젝트 ID 통일**: config.toml을 `zayhfjuwviporbzokudr`로 변경하거나, `.env.production`을 `dmyjpvxhlwmrqqqisbjj`로 변경
2. **service_role 키 로테이션**: `.env`가 git 히스토리에 있으면 키 즉시 교체
3. **verify_jwt = true로 변경**: 최소한 `create-test-user`는 반드시

### 단기 (P1)
4. **E2E 재검증**: 올바른 Supabase 프로젝트에서 Edge Functions 배포 후 재테스트
5. **CORS 도메인 제한**: `*` → `https://insuro.biz, https://www.insuro.biz`
6. **환경 변수 관리**: `.env`를 `.gitignore`에 추가, 시크릿 매니저 도입

### 중기 (P2)
7. **Google OAuth 우회 E2E**: 테스트 전용 이메일/비밀번호 계정 생성하여 자동화 검증 경로 확보
8. **Edge Function별 JWT 검증 감사**: 15개 함수 각각의 인증 로직 확인
9. **네이버 검색광고 API 키 로테이션**: 평문 노출 이력 있으므로 교체

---

## 7. Phase 2 검증에 대한 최종 판정

| 항목 | 판정 | 비고 |
|------|------|------|
| 검증 방법론 | **불충분** | API 호출만으로 E2E라 칭하는 것은 부적절 |
| Edge Function 분석 | **오류** | 프로젝트 ID 불일치를 발견하지 못함 |
| 보안 분석 | **미수행** | OWASP 기준 최소 5개 이슈 미발견 |
| 검증 커버리지 | **33%** | 15개 Edge Functions 중 5개만 테스트 |
| 결론의 정확성 | **부분 정확** | "미배포"는 맞을 수 있으나 원인 분석 부재 |

**신뢰도: LOW** — Phase 2 결과를 의사결정 근거로 사용하기에 불충분하며, 프로젝트 ID 통일 후 재검증 필요.

---

**검토자**: Loki (Devil's Advocate / 레드팀 팀장)
**검토 일시**: 2026-04-23
