smishing-stop-mcp

jeongmk522-netizen/smishing-stop-mcp
0 starsCommunity

Install to Claude Code

This server doesn't publish a one-line install command. Follow the setup in the source repository.

Summary

Analyzes suspicious messages to detect smishing, messenger phishing, and voice phishing attempts, providing risk assessment, evidence, and action guides in Korean.

README.md

피싱멈춰 — 스미싱 판별기 MCP 서버

카카오 PlayMCP 등록용 원격 MCP 서버입니다. 의심스러운 문자/메시지를 받아 스미싱·메신저피싱·보이스피싱 유도 여부를 판정하고, 근거와 상황별 대응을 한국어로 돌려줍니다. (카카오 AGENTIC PLAYER 10 출품작)

보안 도구의 본질상 오판이 치명적이므로, 이 서버는 정확성과 보수성을 최우선으로 설계했습니다. 절대 "100% 안전"이라고 단정하지 않으며, 불확실하면 최소 "의심" 또는 "판단보류"로 내립니다.

---

1. 핵심 설계: 2층 구조

스미싱 문구는 끝없이 변형됩니다. 그래서 문구를 일일이 나열하지 않고 두 개의 층으로 나눠 판단합니다.

 입력 메시지
    │
    ▼
┌──────────────────────────────────────────────────────────────┐
│ 1층 — 확정 신호 (닫힌 데이터 조회, 결정론적)                       │
│   · URL 추출 → 단축URL 펼치기 → KISA 피싱 차단목록 / Google      │
│     Safe Browsing / KISA WHOIS 도메인 나이 조회                  │
│   · 발신번호: 국제발신(+), 070, 변작 의심                         │
│   ★ KISA/Safe Browsing 매칭 = '악성 확정' → verdict '위험' 고정    │
└──────────────────────────────────────────────────────────────┘
    │  (확정 신호는 아래 2층이 절대 덮어쓰지 못함)
    ▼
┌──────────────────────────────────────────────────────────────┐
│ 2층 — 사회공학 추론 (점수에만 기여)                               │
│   · 휴리스틱(키 불필요): 긴급성·결제/환급·기관 사칭·링크 유도·       │
│     APK 설치·인증번호/계좌 요구·지인 사칭의 '본질'을 카테고리로 판별 │
│   · LLM(선택): 새로운 표현·우회 문구·맥락 사칭을 보완(0~1 위험도)    │
└──────────────────────────────────────────────────────────────┘
    │
    ▼
  점수 합산(0~100) → 3단계 판정(위험 / 의심 / 판단보류)
    + 근거 · 탐지 URL · 상황별 대응 · 위젯 카드 · 면책

불변 규칙: 2층 추론은 점수에만 기여하며, 1층의 확정 악성 신호를 절대 덮어쓰지 못합니다. 1층에서 악성 URL이 확정되면 텍스트가 정상으로 보여도 "위험"으로 고정됩니다.

보수성(안정성) 규칙

  • "안전" 단정 출력 금지. 가장 낮은 등급도 "판단보류"입니다.
  • 거짓 음성(놓침)을 거짓 양성보다 더 위험하게 취급합니다.
  • 링크 평판을 검증할 수 없는 상태에서 사회공학 정황이 함께 발견되면 보수적으로 "의심"으로 상향합니다.
  • 분석 중 예외가 나도 절대 "안전"으로 비치지 않게 "판단보류"를 반환합니다.

Graceful degradation (키 없이도 동작)

모든 외부 API 키는 선택입니다. 키가 없으면 해당 데이터 소스만 비활성화되고, 사용 가능한 신호만으로 판정하되 data_sources에 상태를 투명하게 표기합니다. 즉 키 0개로도 서버가 정상 작동합니다(2층 휴리스틱 + 발신번호/단축URL 분석).

---

2. 도구 스펙: check_smishing

입력

| 필드 | 타입 | 필수 | 설명 | |---|---|---|---| | message_text | string | ✅ | 분석할 의심 메시지 원문 | | sender | string | | 발신번호 또는 발신자명 (예: +1 213-..., 070-..., 국민은행) | | clicked | boolean | | 링크 클릭 여부 (알 때만) | | installed | boolean | | 앱/APK/파일 설치 여부 (알 때만). true면 즉시 대처 가이드로 전환 |

출력 (구조화 JSON)

| 필드 | 타입 | 설명 | |---|---|---| | risk_level | "위험" \| "의심" \| "판단보류" | 3단계 판정 | | score | number (0~100) | 위험 점수 | | reasons | string[] | 판정 근거(점수 기여 순) | | detected_urls | {url, expanded_url?, verdict, source, detail?}[] | URL별 평판. verdict: 악성확정/의심/확인불가/특이사항없음 | | action_guide | string[] | 상황별 대응 | | follow_up_questions | string[] | 호스트가 조건부로 되물을 질문(비면 되묻지 않음) | | card | object | 카카오툴즈 위젯 카드용 구조(배지/근거/대응 버튼) | | data_sources | object | 각 소스의 사용/비활성 상태(투명성) | | disclaimer | string | 면책 및 신고 안내 |

인터뷰 분기(편의성 보호)

  • 호스트(LLM)는 먼저 message_text만으로 호출합니다. 미리 캐묻지 않습니다.
  • 결과의 follow_up_questions비어 있지 않을 때만 사용자에게 되묻습니다. 이 배열은 판정이 위험/의심이고 clicked/installed가 미상이라 출력이 바뀔 수 있을 때만 채워집니다. 비어 있으면 되묻지 않습니다.
  • installed=true로 다시 호출되면 action_guide즉시 대처 모드(모바일 백신 검사, APK 삭제, 통신사 소액결제 차단, 118 신고, 금전 피해 시 112)로 전환됩니다.

---

3. 빠른 시작 (로컬 실행)

요구사항: Node.js ≥ 18 (개발 환경은 Node 22/25에서 검증).

# 1) 의존성 설치
npm install

# 2) 환경변수(선택) — 키 없이도 동작합니다
cp .env.example .env
#   필요하면 .env 에 KISA_WHOIS_SERVICE_KEY / GOOGLE_SAFE_BROWSING_API_KEY /
#   ANTHROPIC_API_KEY / KISA_PHISHING_CSV_PATH 를 채웁니다.

# 3) 빌드 + 실행
npm run build
npm start            # 기본 http://localhost:3000 , MCP 엔드포인트 POST /mcp

# 개발 모드(핫리로드)
npm run dev

# 오프라인 자체 검증(네트워크 없이 판정 로직 점검)
npm run selftest

동작 확인

# 헬스체크
curl http://localhost:3000/healthz

# MCP 도구 호출 (Streamable HTTP는 Accept 헤더 두 개를 요구)
curl -s -X POST http://localhost:3000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"check_smishing","arguments":{"message_text":"[CJ대한통운] 주소불일치로 배송보류. 앱 설치 후 확인 http://bit.ly/abcd","sender":"+1 213-555-0199"}}}'

---

4. 환경변수

| 변수 | 기본값 | 설명 | |---|---|---| | PORT | 3000 | 서버 포트 | | INBOUND_API_KEY | (없음) | 설정 시 인바운드 X-API-Key(또는 Bearer) 검사. PlayMCP 커스텀 헤더 인증 대응 | | CORS_ORIGIN | | CORS 허용 오리진( 또는 콤마구분) | | OFFLINE_MODE | false | true면 모든 외부 호출 차단(테스트용) | | FETCH_TIMEOUT_MS | 4000 | 외부 호출 타임아웃 | | MAX_REDIRECT_HOPS | 5 | 단축 URL 펼치기 최대 hop | | KISA_PHISHING_CSV_PATH | ./data/kisa_phishing_urls.csv | KISA 피싱 URL CSV 로컬 경로 | | EXTRA_BLOCKLIST_DOMAINS | (없음) | 보강/테스트용 악성 도메인(콤마구분) | | KISA_WHOIS_SERVICE_KEY | (없음) | 공공데이터포털 KISA WHOIS serviceKey | | GOOGLE_SAFE_BROWSING_API_KEY | (없음) | Google Safe Browsing v4 키 | | ANTHROPIC_API_KEY | (없음) | 2층 LLM 추론용(선택) | | LLM_MODEL | claude-haiku-4-5-20251001 | 2층 LLM 모델(경량 권장) | | ANTHROPIC_BASE_URL | https://api.anthropic.com | Anthropic API 베이스 URL |

---

5. 외부 API 활용신청 방법

각 API의 스펙은 공식 문서로 검증했습니다. 검증 결과와 출처는 아래에 명시합니다.

5-1. KISA 피싱사이트 URL 차단목록 (공공데이터포털, 데이터셋 15109780 / 최신 15143094)

  • 검증 결과: 이 데이터셋은 REST 오픈API가 아니라 CSV "파일데이터" 다운로드입니다. serviceKey나 실시간 조회 엔드포인트가 존재하지 않습니다. (출처: <https://www.data.go.kr/data/15109780/fileData.do>, 최신본 <https://www.data.go.kr/data/15143094/fileData.do>)
  • 활용신청: 파일데이터는 로그인 없이 즉시 다운로드, 무료, 별도 승인 불필요.
  • 사용법:
  1. 위 data.go.kr 페이지에서 CSV(2컬럼: 날짜, URL)를 다운로드합니다.
  2. 파일을 KISA_PHISHING_CSV_PATH(기본 ./data/kisa_phishing_urls.csv)에 둡니다.
  3. 서버 재시작 → 차단목록 로드(/healthzblocklist.size로 확인).
  • 도우미: node scripts/update-kisa-db.mjs (수동 다운로드 안내. KISA_CSV_DOWNLOAD_URL 환경변수로 직링크 자동화 가능)
  • 키가 없어도 EXTRA_BLOCKLIST_DOMAINS로 보강 가능합니다.

5-2. KISA WHOIS 오픈API (도메인 등록일/나이, 공공데이터포털 15094277)

  • 검증 결과: REST API. GET http://apis.data.go.kr/B551505/whois/domain_name?serviceKey=...&query=<도메인>&answer=json, 응답에 regDate(등록일)·endDate(만료일) 포함. ⚠️ .kr/.한국 국가도메인만 조회 가능(국제 TLD 불가). (출처: <https://www.data.go.kr/data/15094277/openapi.do>, <https://whois.kr/kor/openkey/keyCre.do>)
  • 활용신청:
  1. <https://www.data.go.kr> 회원가입/로그인.
  2. "인터넷주소(도메인, IP) 정보검색 서비스"(데이터셋 15094277) 검색 → 활용신청(개발/운영계정, 자동승인).
  3. 마이페이지 > 오픈API > 개발계정 상세에서 serviceKey 발급(Encoding 키 권장) → .envKISA_WHOIS_SERVICE_KEY에 입력.
  • 한도: 개발계정 일 10,000건, 운영계정 활용사례 등록 시 최대 일 100,000건.

5-3. Google Safe Browsing API (v4 Lookup)

  • 검증 결과: POST https://safebrowsing.googleapis.com/v4/threatMatches:find?key=API_KEY. 요청 threatInfo.threatEntries[{url}], 응답 matches[](비면 미탐). threatTypeSOCIAL_ENGINEERING이 피싱 신호. 무료. (출처: <https://developers.google.com/safe-browsing/v4/lookup-api>) 참고: v4는 deprecated(동작 유지) — 향후 v5 hashes:search 또는 Web Risk로 이전 필요.
  • 발급:
  1. Google Cloud Console 프로젝트 생성/선택.
  2. Safe Browsing API 사용 설정.
  3. 사용자 인증 정보 > API 키 생성 → .envGOOGLE_SAFE_BROWSING_API_KEY에 입력.

5-4. 단축 URL 펼치기 (키 불필요)

  • 리다이렉트 자동추적을 끄고 Location 헤더를 hop마다 직접 검증하며 최대 N회 따라갑니다. SSRF 방어: 각 hop의 호스트가 사설/내부/클라우드 메타데이터 대역(10/8,127/8,169.254/16,172.16-31,192.168/16, IPv6 ULA 등)이면 차단, http/https만 허용, 타임아웃·hop 상한 적용, 최종 페이지 본문/JS는 실행하지 않습니다.

5-5. 2층 LLM (선택, Anthropic)

  • 없으면 2층은 휴리스틱만으로 동작합니다. 발급: <https://console.anthropic.com> → API 키 → .envANTHROPIC_API_KEY. 분류 작업이므로 경량 모델(claude-haiku-4-5-...) 권장.
  • 호스트 LLM 샘플링(sampling/createMessage)은 광범위하게 지원되지 않으므로 의존하지 않습니다.

---

6. 배포 — PlayMCP in KC (공식 경로)

출처: PlayMCP 공식 공모전 가이드(Notion). Agentic Player 10 예선은 반드시 카카오가 제공하는 PlayMCP in KC(MCP 서버 배포 서비스, 무상)로 배포해야 합니다. 이 서버의 Dockerfile이 그대로 사용됩니다(저장소 루트에 Dockerfile 존재).

⚠️ PlayMCP in KC 발급은 예선 기간(2026-06-15 ~ 07-14)에만 가능합니다. 심사 시간을 고려해 미리 발급하세요. 계정당 최대 2대.

경로 A — Git 소스 빌드 (권장)

  1. 이 프로젝트를 GitHub 등 Git 저장소에 push (저장소 루트에 Dockerfile 포함 — 이미 있음).
  2. <https://playmcp.kakaocloud.io> 접속 → 카카오 계정(=PlayMCP 가입 계정) 로그인.
  3. + 새 MCP 서버 등록Git 소스 빌드 선택 후 입력:
  • MCP 서버 이름 / 설명: 자유(PlayMCP 표시와 무관).
  • Git URL: 저장소 주소. (private이면 PAT 입력, public이면 비움)
  • 브랜치/ref: 보통 main.
  • Dockerfile 경로: 보통 Dockerfile.
  1. 등록하기 → Status Starting → 수십 초~수 분 후 Active.
  2. Active 서버 상세에서 Endpoint URL 복사 → 7장에서 PlayMCP 등록 시 사용.

경로 B — 컨테이너 이미지

로컬에서 docker build 후 이미지를 등록하는 방식도 지원합니다(PlayMCP in KC의 "컨테이너 이미지로 MCP 서버 등록하기" 가이드 참고).

(참고) 자체 호스팅 대체 경로

원리상 공개 HTTPS Streamable HTTP URL이면 동작하므로, 개발/테스트 단계에서는 Railway/Render/Fly.io나 임의 Docker 호스트로도 띄울 수 있습니다. 단, 공모전 예선 제출은 PlayMCP in KC Endpoint를 써야 합니다. 컨테이너에는 /healthz HEALTHCHECK가 포함돼 있습니다.

---

7. PlayMCP 등록 & 예선 접수 절차 (공식)

  1. 로컬에서 개발·테스트 완료 (npm run selftest, MCP Inspector 권장 — 아래 8장).
  2. PlayMCP in KC에서 배포 → Endpoint URL 획득(6장).
  3. PlayMCP 등록: <https://playmcp.kakao.com/console> 로그인 → 새로운 MCP 서버 등록MCP Endpoint에 위 Endpoint URL 입력 → 정보 불러오기 클릭.
  • ⚠️ "정보 불러오기"가 성공해야 합니다. 실패하면 MCP 서버에 문제가 있는 것입니다(스펙 미준수 등).
  • 인증이 필요하면 OAuth 또는 커스텀 헤더(이 서버는 INBOUND_API_KEY 설정 시 X-API-Key 검사)를 사용합니다.
  1. 정보 입력 후 임시 등록 클릭. (이 단계에서 "등록 및 심사요청"을 누르지 말 것)
  2. MCP 상세 미리보기도구함에 추가 → PlayMCP AI 채팅으로 충분히 테스트.
  3. 테스트 완료 후 임시 등록 상태에서 심사 요청. (영업일 1~2일, 최대 7일)
  • 반려 시 카카오계정 대표이메일로 사유 발송 → 수정 후 재요청.
  1. 승인되면 공개 상태가 "나에게만 공개""전체 공개"로 전환.
  2. 전체 공개된 MCP 상세페이지 주소 복사 (예: https://playmcp.kakao.com/mcp/12345...).
  3. 공모전 페이지(<https://b.kakao.com/views/PlayMCP/AGENTIC_PlAYER_10>) → Player 예선 참여 → 비즈폼 접수(최대 2개 MCP 등록 가능).

일정: 예선 접수 2026-06-15 ~ 07-14, 본선 카카오톡 공개투표 2026-08-31~09-28, 시상 2026-10-23. 평가 창의성·편의성·기술 안정성.

---

8. ✅ PlayMCP 개발가이드 준수 체크리스트

공식 개발가이드(2026-06-12 기준)의 필수 요건과 본 서버의 충족 상태입니다. (가이드: <https://kko.to/PlayMCPdevguide>)

| 요건 | 상태 | |---|---| | MCP 지원버전 2025-03-26 ~ 2025-11-25 | ✅ SDK 1.29가 범위 내 협상(테스트 시 2025-06-18) | | Streamable HTTP만 지원 | ✅ StreamableHTTPServerTransport | | Remote, 공개 URL | ✅ PlayMCP in KC Endpoint | | Stateless 권장 | ✅ sessionIdGenerator: undefined | | 인증: OAuth 또는 커스텀 헤더 | ✅ 커스텀 헤더(X-API-Key, 선택) | | 서버/툴 이름에 "kakao" 미포함 | ✅ smishing-stop-mcp / check_smishing | | 툴 이름: 1~128자, [A-Za-z0-9_-] | ✅ check_smishing | | 툴 개수 ≤ 20 (권장 3~10) | ⚠️ 현재 1개(집중형). 필요 시 보조 툴 추가 가능 | | 필수 property: name·description·inputSchema·annotations | ✅ annotations 5개 힌트 모두 지정 | | annotations: title·readOnlyHint·destructiveHint·openWorldHint·idempotentHint | ✅ true/false/true/true | | description ≤ 1,024자 + 서비스명 영/국문 병기 | ✅ 991자, Smishing-Stop(피싱멈춰) 포함 | | result 크기 최소화 / error·비위젯은 마크다운 텍스트 | ✅ 간결한 text 폴백 + 구조화 content | | 툴 응답 p99 ≤ 3,000ms 필수 | ✅ 전체 데드라인 OVERALL_DEADLINE_MS(기본 2800ms)로 강제 | | 툴 평균 응답 ≤ 100ms | ⚠️ 외부 키 미설정 시 로컬(KISA CSV+휴리스틱)으로 <10ms. 외부 API 사용 시 지연↑ — 아래 참고 | | 광고 유도 금지 | ✅ 해당 없음 | | MCP Inspector 사전 점검 | ☐ 제출 전 직접 수행 권장: npx @modelcontextprotocol/inspector |

평균 100ms vs 정확도 트레이드오프: 외부 평판 API(Safe Browsing/WHOIS) 및 단축 URL 펼치기는 네트워크 지연이 있습니다. 엄격한 평균 100ms가 필요하면 외부 키를 비우거나 OFFLINE_MODE=true로 두어 로컬 결정론 모드(KISA CSV + 휴리스틱, <10ms)로 운용하세요. 정확도를 우선하면 키를 채워 전체 2층을 가동하되, p99는 데드라인으로 3초 이내가 보장됩니다.

남은 ⚠️ 확인 필요 항목 (비공개/미검증)

  1. 카카오툴즈 위젯 카드(widget json)의 정확한 스키마: 개발가이드는 "tool 결과가 widget json이 아니면 마크다운 텍스트 권장"이라고만 명시하고, widget json의 구체 규격은 공개돼 있지 않습니다. 본 서버는 호스트가 카드 규격을 몰라도 risk_level/reasons/action_guide로 렌더되도록 설계하고 card 구조화 필드(배지/버튼)를 함께 제공합니다. 결선 진출 시 카카오 제공 규격에 맞춰 buildCard()를 조정하세요.
  2. KISA WHOIS regDate 날짜 포맷(14자리 추정) 미검증 → parseWhoisDate()가 다중 포맷 방어 파싱. 실제 응답으로 확인 권장.
  3. 사기 전화번호/계좌 조회 API: 안정적 공개 무료 오픈API 가용성 불확실 → 제외, 발신번호 형식 휴리스틱으로 대체(추후 추가 여지).
  4. KISA 피싱 CSV 자동 다운로드 직링크: data.go.kr 파일 다운로드는 세션 의존 → 수동 다운로드 기본 + KISA_CSV_DOWNLOAD_URL 자동화 옵션.
  5. Safe Browsing 정확한 쿼터 수치: 공식 미게시 → Cloud Console에서 확인.

---

9. 프로젝트 구조

src/
├── index.ts                 진입점: Express + Streamable HTTP(stateless), 인증/CORS/헬스체크
├── server.ts                McpServer 구성 + check_smishing 등록
├── config.ts                환경변수 + 점수 가중치(WEIGHTS) 중앙 집중
├── types.ts                 공용 타입(출력 스키마와 1:1)
├── util.ts                  타임아웃 fetch, 호스트/IP 파싱, SSRF 차단
├── tool/
│   ├── schema.ts            입력/출력 zod shape + 도구 설명(호스트 행동 규칙)
│   └── checkSmishing.ts     핸들러: structuredContent + 텍스트 폴백, 예외 안전
├── pipeline/
│   ├── index.ts             6단계 오케스트레이션
│   ├── parse.ts             URL/발신번호/키워드 추출(NFC 정규화, 한글 안전)
│   ├── scoring.ts           점수 합산 + 3단계 판정 + 보수성 규칙
│   └── actionGuide.ts       상황별 대응 + 되묻기 + 카드 빌더
├── layer1/                  확정 신호(결정론적)
│   ├── urlReputation.ts     URL별 KISA+SafeBrowsing+WHOIS 종합
│   ├── unshorten.ts         단축 URL 펼치기 + SSRF 방어
│   ├── kisa.ts              피싱 CSV 로더 + 호스트 매칭
│   ├── safeBrowsing.ts      Google Safe Browsing v4 클라이언트
│   ├── whois.ts             KISA WHOIS 도메인 나이
│   └── sender.ts            발신번호 분석
└── layer2/                  사회공학 추론(점수 기여)
    ├── heuristics.ts        카테고리 기반 결정론적 판별
    └── llm.ts               선택적 Anthropic 추론(graceful)

---

10. 한국어 인코딩

  • 입력을 NFC 정규화하여 자모 분해로 인한 키워드 매칭 누락을 방지합니다.
  • 토크나이저에 의존하지 않고 유니코드 인지 정규식으로 직접 스캔합니다(한글이 빈 토큰이 되지 않음).
  • Express express.json() + LLM 호출은 모두 UTF-8로 처리합니다.

11. 면책

본 도구의 결과는 참고용이며 최종 판단과 책임은 본인에게 있습니다. 스미싱 의심 신고 118(불법스팸대응센터), 금전 피해 발생 시 112(경찰), 개인정보 노출 시 KISA 보호나라(privacy.kisa.or.kr) 명의도용 차단 서비스를 이용하세요.

라이선스: MIT

Related MCP servers

Browse all →