keeper-out 1만원 게이트 검증 시스템 — 설계 보고

2026-05-05 · keeper-direct staging · 상태 설계 — 코드 수정 대기 · 선행 4건 docs docs/keeper-out/anti-abuse/01~04.md

목차

  1. 한 줄 요약 (BLUF)
  2. 현황 — 지금 1만원이 어떻게 빠지나
  3. 위협 모델 — 누가 어떻게 어뷰징하나
  4. 검증 차원 (V1~V11) MUST/SHOULD/OUT
  5. 종합 게이트 흐름 — "지원금 받기" 클릭 후 영업원 모달
  6. 키퍼메이트(Referio) RE — 1차 가설 폐기 후 정정
  7. 키퍼 admin RE — 자동님 권한·데이터 모델·사후 검수 쿼리
  8. dual-layer 사후 검수 (★ keeper-out 고유)
  9. 자동님이 이미 결정한 것
  10. 결정 대기 중 + 다음 액션

01.한 줄 요약 (BLUF)

지금 /diagnose 제출은 4개의 약한 가드만 통과하면 즉시 사장님 폰으로 1만원이 발사됨. 영업원·디바이스·IP·OTP·잔액 preflight 어느 것도 박혀 있지 않다. 본 보고서는 OUTBOUND(영업원 매장방문) 컨텍스트에 맞는 3개 동시 게이트(영업원 이름+코드 / 사장님 폰 OTP / naver_place_id × phone 유일성) + 1개 비동기 가드(잔액·이상치) + dual-layer 사후 검수(영업원 30k 인센은 24h~3일 후 확정) 구조를 제안한다. 자동님은 이미 키퍼 플랫폼 admin 권한(ceo@bizdirect.co.kr, group=13 "키퍼 아웃바운드")을 보유 중이라, 사후 검수의 ②③룰을 한 번의 admin BFF 호출로 즉시 매핑 가능.

★ 결정적 인사이트 3개

02.현황 — 지금 1만원이 어떻게 빠지나

호출 사슬:

// app/api/keeper-out/diagnose/route.ts (현재 production)
POST /api/keeper-out/diagnose
  → 필드 non-null (phone, current_security, ceo_name, marketing_consent)
  → sbInsert keeper_outbound_leads // 23505 → 409 "이미 진단 받으신 매장"
  → funnel_event "diagnose_submitted"
  → payouts 3건 INSERT // 사장님 10k + promoter 10k + HQ 10k
  → scheduled_sms_at = now() + 5min
  → ★ fire-and-forget sendNaverPayCoupon(phone, 10000)
       → bizapi.giftishow.com/bizApi/send api_code=0204
       // 실패해도 200 OK, funnel_event "naver_pay_failed" 만 기록

현재 적용된 가드 (모두 약함)

#위치검증우회
G1route.ts:14-234개 필드 non-null아무 문자열 박으면 통과
G2idx_leads_naver_place_unique (partial)naver_place_id 동일 시 409naver_place_id 없이 제출하면 무력화
G3RLS anon_insert_leads(없음 — with check (true))익명 키만 있으면 우회
G4giftishow.ts:92-100지원 금액 화이트리스트우회 불가 (10k 외 NO_GOODS)

Attack surface 6종

03.위협 모델 — Actor × Scenario

IDActor시나리오현재 차단?손실 단가
T1외부 사례비 헌터폰 N개로 무한 제출10k × N
T2외부 봇/스크립트API 자동 POST10k × ∞
T3영업원 본인본인/지인 폰 5대로 5번 (본인 30k + 지인 10k)40k × 5
T4영업원 단순 실수자동완성 미수행 (G2 무력화)10k × N
T5매장 사장 어뷰징본인+가족 폰 5대로 5번10k × 4
T6영업원 의도적 오발송매장 안 갔는데 발송 (실적 부풀리기)40k
T7거짓 진단 데이터"모름"·랜덤값으로 통과 → 1만원10k
T9review-on cross-bleed같은 Giftishow 잔액 공유가변
T10사일런트 발송 실패잔액 0 / 인증 만료 못 알아챔UX 손실

고우선: T1·T2·T3·T7. 필수: T4·T5·T6. 운영 가시성: T9·T10.

04.검증 차원 (V1~V11)

V이름막는 위협OUTBOUND 마찰티어
V1본인성 — SMS OTPT1·T2·T3·T5낮음 (사장님 옆 30초)MUST
V2현장성 #1 — GPS × place 거리 ≤200mT2·T4·T6미미 (이미 수집 중)MUST
V3현장성 #2 — 영업원 이름+5자리 코드T3·T6·T710초 입력MUST ★ 자동님 지시
V4유일성 — naver_place_id NOT NULL + phone unique lifetimeT5·T8·중복없음MUST
V5예산 — 잔액 preflight + capT2·T9·T10없음MUST
V6사진 EXIF 시간 검증T6·T7약함SHOULD
V7패턴 이상치 탐지 (cron)T3·T7 누적없음 (감사용)SHOULD
V8매장 실재성 외부 검증T6·T8미미SHOULD
V9디바이스/IP 핑거프린팅T2부분적NICE (기록만)
V10CAPTCHA / TurnstileT2큼 (영업 흐름 깨짐)OUT (인바운드만)
V11사업자등록번호 NICE/홈택스T6미미OUT (1만원 게이트)

05.종합 게이트 흐름 (자동님 5/5 정정 반영)

UX 정책: 사장님이 진단 끝까지 채우고 지원금 받기 버튼을 누른 그 시점에 영업원 이름+코드 입력 모달 등장. 진단 단계와 영업원 게이트 화면을 명확히 분리.

[클라이언트 — 사장님이 진행] 매장 자동완성 (naver_place_id 강제) → Q1 보안업체 → Q2 약정연차 → 카메라/요금 → Q4 통신사 → pain step → 사진 → 사장님 이름·폰 → [지원금 받기 1만원] 버튼 (여기까지 키 발사 X — form state) │ 버튼 클릭 [클라이언트 — 영업원 화면 등장 (모달/풀스크린)]"본사 확인 절차" 톤으로 사장님께 보임 영업원 이름 영업원 코드 5자리 (mixed case, 예: K9bR2) → [확인] │ POST /api/keeper-out/diagnose/request-otp [서버 게이트 0: pre-validate] SMS 비용 발생 전 모든 정합성 1차V3 영업원 이름+코드 매칭 + active + day cap (제일 먼저) V4 naver_place_id NOT NULL + DB unique V5 budget preflight (시간/일 cap + giftishow 잔액) V8 place 실재성 V2 GPS 거리 ≤ 200m → 모두 통과 시 OTP 발송 (balerion SMS proxy) 사장님 폰에 OTP 도착 POST /api/keeper-out/diagnose/verify [서버 게이트 1: identity] V1 OTP 검증 (5분 만료, 3회 lock) V6 EXIF 시간 검증 (soft, agent confirm 옵션) → leads INSERT + funnel_event "diagnose.verified" [서버 게이트 2: dispatch] V5 잔액 재확인 sendNaverPayCoupon → 사장님 1만원 즉시 영업원 30k 인센은 status='pending'으로만 기록 [비동기 — 사후 검수: 영업원 30k 회수 윈도우] 24h: TM 콜백 → 사장님 연락 가능 여부 (키퍼메이트 ① 룰) 1~3d: 사업자 확인 + 도입 의향 (키퍼메이트 ②③ 룰) 통과 → payouts.lead_30k 'confirmed' 실패 → 'invalid' + 영업원 invalid률 누적 V7 야간 cron 이상치 탐지 → 텔레그램 데일리 리포트 V9 ip/UA 메타 적재
★ 왜 영업원 게이트가 "지원금 받기" 클릭 후인가
★ 왜 영업원 게이트가 OTP보다 앞인가

06.키퍼메이트(Referio) RE — 1차 가설 폐기

⚠ 1차 가설 정정
어제 가설 "키퍼메이트 = keeper-partner 모바일앱" 폐기. 키퍼메이트는 Puzl(퍼즐코퍼레이션)이 운영하는 Referio라는 별개 SaaS. 자동님 지메일에서 가입승인(2026-03-04, 추천코드 86Af9) 확인.

키퍼 플랫폼 vs 키퍼메이트(Referio) 분리

A. 키퍼 플랫폼B. 키퍼메이트(Referio)
운영 주체한화비전 자체Puzl(puzl.co.kr), 한화비전이 클라이언트
도메인*.keeperplatform.comreferio.puzl.co.kr + 랜딩 keeper.ceo/keeper-mate
사용자본사·매장·설치·영업파트너(B2B)콘텐츠 작성자(블로그·SNS referrer)
백엔드10 마이크로서비스Supabase (eqdnirtgmevhobmycxzn)
본인인증Danal/PASS + 카카오 OAuth없음 (사후 본사 수동 승인)

키퍼메이트 보상·정책 (랜딩 페이지 직접 추출)

단계보상검증
유효 리드1.5만원① 연락 가능 ② 사업자 확인 ③ 도입 의향
계약 성사+2만원키퍼 서비스 계약 체결
합계건당 3.5만원+ 4티어 활동비 (Authorized/Silver/Gold/Platinum: 0/5/10/30만/월)

키퍼메이트 어뷰징 정책 (FAQ Q5 명문화) ★

"허위/과장 광고, 검색 광고 집행, 스팸성 홍보, 자기 추천(셀프 가입) 등은 금지됩니다. 위반 시 키퍼 메이트 자격이 해지될 수 있습니다."

"자기 추천 금지"는 우리 위협 T3(영업원 본인/지인 폰)과 정확히 같은 시나리오 — 정책 차용 + 자동 검출 둘 다 박는다.

07.키퍼 admin RE — 자동님 권한 + 데이터 모델

자동님 admin 권한 (★ ceo@bizdirect.co.kr)

API 백엔드 도메인 분리 (마이크로서비스)

api-account.keeperplatform.com      // 계정/사용자 (v1/account/*)
api-notification.keeperplatform.com // 알림/SSE/푸시
admin.keeperplatform.com/api/my/*   // BFF — same-origin 프록시
admin.keeperplatform.com/api/keeper-auth  // 인증 어댑터

CORS: api-account는 Access-Control-Allow-Origin: * + Bearer JWT cookie 자동 첨부. BFF: /api/my/group, /api/my/method-permission?method=get&rootPath={X}.

User (OWNER) 데이터 모델 — 4136명

interface User {
  id: number;                       // 6245 (auto-increment)
  name: string;                     // 실명
  loginInfo: {
    socialProvider: null | string;  // "kakao" 등
    email: string;
    id: string | null;             // login id (qbflel/khw491)
  };
  email: string;
  mobile: string;                   // "010-3111-1165" (대시 포함) ★ 매칭 키
  memo: string | null;
  createdAt: string;
  shopName: string;                 // 대표 매장명
  shopAddress: string;
  shops: any[];                    // 다매장 점주
  subscribes: any[];                // ★ 비면 가입만 / 있으면 계약 성사
}
// 회원번호: KP{YY}{MM}{DD}{HH}{MM}{ID4}  (예: KP26050518592567)
// 역할 enum: OWNER (자영업자) / PARTNERS (영업파트너=대리점)

Order (설치) 데이터 모델 — 42건

08.dual-layer 사후 검수 (★ keeper-out 고유)

키퍼메이트는 단일 검수 후 발송이라 dual-layer 불필요. 우리는 사장님 1만원 즉시 발송이라 영업원 30k 인센은 별도 회수 윈도우 필수.

// [24h cron — TM 통화 후]
GET /v1/account/admin/users?role=OWNER&phone={leads.phone}
  → items.length === 0
      // 사장님이 키퍼 가입 안 함 = ②③룰 미달
      payouts.lead_30k status = 'invalid'
      영업원 invalid률 누적
  → items.length > 0
      // 가입 확인 = ②룰 통과
      → user.subscribes.length > 0
          → ③룰 통과 = 30k 인센 'confirmed'
      → user.subscribes.length === 0
          → 7일 추가 대기 (가입했지만 미구독)

// [7일 cron]
GET /v1/order/admin/purchase?keyword={leads.store_name}
  → 설치 완료 주문 hit
      → 인센 'confirmed' + install 50k 인센 자동 트리거

// [30일]
미확정 = 영업원 30k 'invalidated_final'

09.자동님이 이미 결정한 것

Q1. agent.code 형식
결정 5자리 영숫자 mixed case (Referio 표준 86Af9 차용)
Q2. ref URL 패턴
결정 https://keeper.ceo/keeper-out?ref={code} 채택 (Referio ?ref= 동일)
Q3. UX 흐름 — 영업원 코드 입력 시점
결정 사장님이 진단 폼 끝까지 + 폰 입력 후 "지원금 받기" 버튼 클릭한 그 시점에 영업원 이름+코드 모달 등장 (사장님 영역/영업원 영역 화면 분리)
Q4. 코드 수정
보류 모든 계획 끝난 후 추후 지시 — 지금은 정찰·문서화만

10.결정 대기 중 + 다음 액션

비결정 5건

#항목옵션
1OTP 발송 채널balerion SMS proxy 그대로 / KISA / NICE / NHN Cloud
2영업원 우회로관리자 PIN 백도어 두기 / 코드 없는 진단 거부
3review-on과 잔액 격리Giftishow 키 별도 가입 / 모니터링만 분리
4agent.code 발급 운영본사 admin 수동 / 자율 가입 후 본사 승인 / Referio 매핑
505-policy.md 어뷰징 정책 명문화 별도 문서예 / 아니오

다음 액션 — 3갈래

C. review-on SSH 진입 — 자동님 본인 시스템 어뷰징 가드 확인

같은 Giftishow 키, 같은 자동님 코드 패턴. balerion.cloud SSH 진입(call-center.pem) → review-on의 어뷰징 가드 차원 매핑.

예상 시간: 1시간 (B 진행 후 부족한 차원 보강용으로 적합)

D. 즉시 hotfix 4건 — 코드 수정 (자동님 보류 지시 중)

naver_place_id NOT NULL 격상 ② phone unique lifetime ③ Giftishow checkBalance preflight ④ 일일 송금 카운터. 모든 계획 끝나면 자동님 지시 후 박을 즉시 효과 큰 4건.

자동님 명시 — "코드수정은 하지말고 문서화 먼저"


11.참조 문서 (in-repo)