Community코딩 & 개발github.com

atokim4/stripe-webhook-security

Stripe 웹훅 엔드포인트를 검사하고 수정합니다. `constructEvent` 서명 검증 없이 Stripe 이벤트를 처리하는 핸들러와 파싱된 본문에 대해 서명을 확인하는 경우를 스캔한 후, 원시 본문, 멱등성, 재생 공격, 비밀 처리 등을 점검합니다.

stripe-webhook-security란 무엇인가요?

이 스킬은 Stripe 웹훅 엔드포인트의 보안을 자동으로 검사하고 수정하는 데 사용됩니다. Next.js, Express, Supabase Edge Functions 등 다양한 플랫폼에서 Stripe 이벤트를 처리하는 핸들러를 식별하고, `constructEvent`를 사용한 서명 검증 누락, 파싱된 본문에 대한 서명 확인 등의 문제를 찾습니다. 또한 원시 본문 사용, 멱등성 처리, 재생 공격 방지, 비밀 키 관리 등 개발자가 직접 검토해야 할 항목에 대한 가이드를 제공합니다. 사용자가 '내 Stripe 웹훅이 안전한가요?'라고 묻거나 'No signatures found matching the expected signature' 오류를 접할 때, 또는 결제를 처리하는 앱을 출시하기 전에 유용합니다. Node 20+ 환경에서 의존성 없이 실행되는 스크립트를 사용하여 프로젝트를 스캔합니다.

지원 대상✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/atokim4/vibe-preflight/tree/HEAD/skills/stripe-webhook-security

Installed? Explore more 코딩 & 개발 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Stripe 웹훅 보안

이 스킬은 vibe-preflight 스캐너(scripts/preflight.mjs, 이 파일과 같은 디렉토리, Node 20+, 의존성 없음, 네트워크 불필요)의 Stripe 부분을 실행합니다. Next.js 라우트 핸들러, pages/api, Express 및 Supabase Edge Functions에서 웹훅 핸들러를 찾아 코드의 두 가지 항목을 검사합니다. 이 페이지의 나머지 부분은 핸들러를 읽어 수행하는 검토 항목입니다. 패턴 스캔만으로는 완벽히 증명할 수 없기 때문입니다.

1. 스캔 실행

프로젝트 루트에서 다음을 실행합니다.

node ${CLAUDE_SKILL_DIR}/scripts/preflight.mjs . --only stripe --fail-on warning --format json

Claude Code는 ${CLAUDE_SKILL_DIR}을 자동으로 채웁니다. 다른 에이전트의 경우 이 스킬의 폴더(예: .agents/skills/stripe-webhook-security 또는 .claude/skills/stripe-webhook-security)를 사용하세요.

규칙수준설명
stripe-webhook-unverifiederror핸들러가 Stripe 이벤트를 처리하면서 constructEvent / constructEventAsync를 호출하지 않습니다. URL을 찾은 누구나 가짜 checkout.session.completed를 POST할 수 있습니다.
stripe-webhook-parsed-bodywarning서명은 확인했지만, 이미 파싱된 본문에 대해 확인했습니다. Stripe는 전송한 정확한 바이트에 서명하므로 실제 이벤트는 400 오류와 함께 실패하고 누군가 결국 검사를 삭제하게 됩니다.

--only stripe는 Stripe 이벤트 이름, stripe-signature 헤더 읽기, 또는 Stripe를 언급하는 파일의 웹훅 경로를 통해 핸들러를 찾습니다. 0 errors를 보고하지만 사용자가 웹훅을 알고 있다면 핸들러를 직접 찾아 아래 검토를 수행하세요.

2. 검증 수정

라우트 핸들러 (Next.js App Router, Remix, Hono 및 기타 fetch 스타일 핸들러):

export async function POST(request: Request) {
  const body = await request.text(); // 원시 본문, 절대 request.json() 사용 금지
  const signature = request.headers.get("stripe-signature");
  if (!signature) return new Response("Missing signature", { status: 400 });
  let event: Stripe.Event;
  try {
    event = stripe.webhooks.constructEvent(body, signature, process.env.STRIPE_WEBHOOK_SECRET!);
  } catch {
    return new Response("Invalid signature", { status: 400 });
  }
  // 이벤트 처리
  return Response.json({ received: true });
}

Express: app.use(express.json()) 이전에 express.raw({ type: "application/json" })로 웹훅 라우트를 등록하고, req.body (이때는 Buffer)를 constructEvent에 전달하세요.

Next.js pages/api: export const config = { api: { bodyParser: false } };로 설정하고 검증 전에 원시 요청을 Buffer로 읽으세요.

Supabase Edge Functions 및 기타 Deno / 엣지 런타임: await stripe.webhooks.constructEventAsync(body, signature, secret, undefined, Stripe.createSubtleCryptoProvider())를 사용하세요.

3. 핸들러를 읽어 나머지 검토

스캐너는 다음 항목을 확인하지 않습니다. 코드를 살펴보고 각 항목에 대해 발견한 내용을 사용자에게 알려주세요.

  • 비밀 키. STRIPE_WEBHOOK_SECRET(whsec_...)은 서버 환경 변수에서 가져와야 하며, 절대 NEXT_PUBLIC_ / VITE_ 변수나 소스 코드에 포함시키지 마세요. 테스트 모드와 라이브 모드는 서로 다른 비밀 키를 가지며, 각 엔드포인트와 stripe listen CLI도 각각 다른 비밀 키를 사용합니다.
  • 재생 공격 방지. constructEvent는 허용 오차(기본값 300초)보다 오래된 서명을 거부합니다. tolerance 인자는 생략하거나 작게 유지하세요. 오류를 잡고 계속 진행하지 마십시오.
  • 멱등성. Stripe는 동일한 이벤트를 두 번 이상 전달할 수 있습니다. 쓰기 작업을 자연스럽게 반복 가능하게 만들거나(Stripe ID를 키로 하는 upsert, 크레딧을 추가하는 대신 상태 설정), event.id를 고유 제약 조건이 있는 테이블에 저장하고 이미 본 ID는 건너뛰세요. 크레딧, 잔액, 재고를 추가하는 부분을 특히 주의 깊게 살펴보세요.
  • 순서. 이벤트가 순서대로 도착하지 않을 수 있습니다. 구독의 경우 이벤트 순서를 신뢰하는 대신 API(stripe.subscriptions.retrieve)에서 현재 객체를 가져오거나 타임스탬프를 비교하세요.
  • 신뢰. 사용자와 계획은 검증된 이벤트(client_reference_id, metadata, 체크아웃 시 저장한 고객 ID)에서 가져오고, 절대 쿼리 문자열 또는 두 번째 서명되지 않은 요청에서 가져오지 마세요. 성공 페이지 리디렉션은 아무것도 증명하지 않습니다. 웹훅에서 액세스 권한을 부여하세요.
  • 응답. 이벤트가 저장되거나 처리되면 즉시 2xx를 반환하세요. 느린 작업은 큐로 보내세요. 잘못된 서명에 대해서만 400을 반환하여 Stripe의 재시도가 의미를 가지도록 하세요.
  • 이벤트 목록. 엔드포인트를 코드가 처리하는 이벤트에만 구독하고, 다른 유형은 오류 없이 무시하세요.

4. 보고

스캐너 결과를 나열하고, 각 검토 항목에 대해 한 줄씩 작성하세요: 양호( fine ), 수정됨( fixed ), 또는 사용자에 대한 질문(예: "크레딧이 모든 전달에 추가됩니다: 반복된 이벤트가 다시 추가되어야 합니까?"). 수정 후 스캔을 다시 실행하고 마지막 줄을 보여주세요. 실제 테스트를 위해 stripe listen --forward-to localhost:3000/api/webhooks와 stripe trigger checkout.session.completed(Stripe CLI 사용)를 제안하세요.

확인된 오탐은 해당 줄 또는 위 줄에 // vibe-preflight-ignore <rule-id>를 추가하고 주석에 이유를 기재하여 무음 처리할 수 있습니다.

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

관련 스킬