Communityコーディング&開発github.com

atokim4/stripe-webhook-security

Stripe Webhook エンドポイントのセキュリティをチェックし修正します。`constructEvent` による署名検証なしで Stripe イベントを処理するハンドラや、解析済みボディに対して署名をチェックするパターンをスキャンし、Raw ボディ、冪等性、リプレイ、シークレットの取り扱いをレビューします。ユーザーが Stripe Webhook を作成・レビューするとき、「Stripe Webhook は安全ですか?」と尋ねられたとき、「期待される署名と一致する署名が見つかりません」というエラーが出たとき、または決済を受け付けるアプリをリリースする前に使用します。

stripe-webhook-security とは?

このスキルは、Stripe Webhook エンドポイントのセキュリティを自動スキャンおよび手動レビューで支援します。Next.js(App Router、pages/api)、Express、Supabase Edge Functions 向けに設計された Node 20+ の依存関係不要なスクリプトを実行し、Stripe イベントを処理するハンドラが `constructEvent` または `constructEventAsync` を使用して署名検証を行っているか、また `req.json()`、`express.json()`、pages/api のデフォルトボディパーサーなどで解析済みのボディに対して署名をチェックしていないかを検出します。スキャンはエラーと警告を報告し、その後、シークレット管理、リプレイ保護、冪等性、イベント順序の保証、信頼できるデータソース、レスポンスの迅速性、サブスクライブするイベントリストといったベストプラクティスを手動レビューするためのガイドを提供します。最終的には、修正後にスキャンを再実行し、必要に応じて Stripe CLI を使った実際のテスト手順を提案します。

対応✓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 Webhook セキュリティ

このスキルは、Stripe に関連する vibe-preflight スキャナー(このファイルの隣にある scripts/preflight.mjs、Node 20+、依存関係なし、ネットワーク不要)を実行します。Next.js ルートハンドラ、pages/api、Express、Supabase Edge Functions 内の Webhook ハンドラを検出し、コード内の 2 つの項目をチェックします。このページの残りの部分は、ハンドラを読んで行うレビューです。パターンスキャンだけでは証明できないからです。

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 に言及しているファイル内の Webhook パスによってハンドラを見つけます。0 errors と報告された場合でも、ユーザーが Webhook を持っていることがわかっていれば、自分でハンドラを見つけて、以下のレビューを行ってください。

2. 署名検証を修正する

ルートハンドラ(Next.js App Router、Remix、Hono などの fetch スタイルハンドラ):

export async function POST(request: Request) {
  const body = await request.text(); // Raw ボディ、絶対に request.json() は使わない
  const signature = request.headers.get("stripe-signature");
  if (!signature) return new Response("署名がありません", { status: 400 });
  let event: Stripe.Event;
  try {
    event = stripe.webhooks.constructEvent(body, signature, process.env.STRIPE_WEBHOOK_SECRET!);
  } catch {
    return new Response("署名が無効です", { status: 400 });
  }
  // イベントを処理
  return Response.json({ received: true });
}

Express: app.use(express.json()) より前に express.raw({ type: "application/json" }) で Webhook ルートを登録し、req.body(この場合は Buffer)を constructEvent に渡します。

Next.js pages/api: export const config = { api: { bodyParser: false } }; と設定し、検証前に Raw リクエストを 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 秒)を超えた署名を拒否します。許容時間の引数は指定しないか、小さく保ってください。エラーをキャッチしてそのまま続行しないでください。
  • 冪等性。 Stripe は同じイベントを複数回配信することがあります。書き込みを自然にリトライ可能にしてください(Stripe ID をキーにした upsert、クレジットを追加する代わりにステータスを設定するなど)。または event.id を一意制約付きのテーブルに保存し、見たことのある ID はスキップしてください。クレジット、残高、在庫を追加する処理は特に注意深く確認してください。
  • 順序。 イベントは順序通りに到着しないことがあります。サブスクリプションの場合、イベントの順序を信頼する代わりに、API から現在のオブジェクトを取得するか(stripe.subscriptions.retrieve)、タイムスタンプを比較してください。
  • 信頼。 ユーザーとプランは検証済みイベント(client_reference_id、metadata、チェックアウト時に保存した顧客 ID)から取得し、クエリ文字列や署名されていない 2 回目のリクエストからは絶対に取得しないでください。成功ページのリダイレクトは何も証明しません。アクセス権は Webhook から付与してください。
  • レスポンス。 イベントが保存または処理されたら、すぐに 2xx を返してください。時間のかかる処理はキューに送ります。400 を返すのは不正な署名の場合のみにし、Stripe のリトライが意味を持つようにしてください。
  • イベントリスト。 エンドポイントはコードが処理するイベントのみにサブスクライブし、他のタイプはエラーにせず無視してください。

4. 報告する

スキャナーが見つけた結果を列挙し、その後レビュー項目ごとに 1 行ずつ: 問題なし、修正済み、またはユーザーへの質問(例: 「配信のたびにクレジットが追加されています: 繰り返しイベントで再度追加されますか?」)を記載します。修正後、スキャンを再実行し、その最終行を表示してください。実際にテストするには、Stripe CLI で stripe listen --forward-to localhost:3000/api/webhooks と stripe trigger checkout.session.completed を提案してください。

確認された誤検出は、該当行またはその上の行に // vibe-preflight-ignore <ルールID> をコメントとともに追加することで抑制できます。

Individual skills in this repo

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

関連スキル