Node.js、Python、Goのための完全なwebhookの例を探します。署名を安全に検証し、リプレイ攻撃を防ぎ、エンドポイントをデバッグする方法を学びます。",

Web Hookの実践的な例: セキュアな実装ガイド

Node.js、Python、Goのための完全なWeb Hookの例を見つけてください。codeで署名を安全に検証し、リプレイ攻撃を防ぎ、エンドポイントをデバッグしてください。

マーティン・ドナディュー

マーティン・ドナディュー

コンテンツマーケター

Web Hookの実践的な例: セキュアな実装ガイド

You’ve got a service that needs to react when something happens somewhere else. A payment clears. A customer record changes. A repo gets a push. You could poll an API every minute and waste cycles asking “anything new?” over and over, or you can let the source system call you when the event happens.

pollingを実行し、1分ごとに「何か新しいものがあるか?」と繰り返し問うことで、サイクルを浪費するのではなく、ソースシステムがイベントが発生したときに呼び出してください。 200、そして完了と呼ぶ。 そのバージョンは、誰かが偽の要求を送信したり、有効な要求を再生したり、フレームワークが署名検証前にボディを解析した場合にハンドラーが破綻するまで、正しく動作します。

このガイドでは、実際に生産環境で使用するパスを示します。 例は小さくてコピーできるように設計されていますが、重要な部分を含んでいます: rawボディの処理、HMAC検証、タイムスタンプのチェック、高速の確認、実用的なデバッグ。

目次

Webhookとは何ですか。なぜ使用するのですか?

請求先のサービスプロバイダーは、請求書を02:13に支払いとしてマークします。アプリが02:14にそれについて知る場合、顧客は即座にアクセスできます。アプリが次のポーリングサイクルで知る場合、顧客は待つことになり、サポートチームにチケットが発行され、ログには無駄なノイズが溜まります。Webhookは、このタイミングの問題を解決するために、イベントが発生したときにHTTPコールバックを送信します。

実際的には、Webhookは、イベント駆動型のPOSTです。プロバイダーは、変更を検出します。たとえば、 invoice.paid, order.createdpush

実際のシステムでは、このパターンが現れるのは、ビジネスイベントに簡潔にマップできるからです。Stripeは支払い結果を投稿します。GitHubはリポジトリの活動を投稿します。Shopifyは注文の更新を投稿します。形状は単純ですが、生産的な動作はそうではありません。金額、アクセス、または在庫を更新するWebhookには、リトライ、重複、信頼できないトラフィックが含まれる場合に、どのパブリックAPIエンドポイントと同じ注意を払う必要があります。

成功するWebhookフローを実現するための心のモデル

Webhookフローの有効な方法は、4つの部分が協力して動作するようにフレームすることです。

  • 元のシステム. イベントを検出するサービス。
  • 宛先エンドポイント. HTTPルートが受け取るもの。
  • イベント. 名前付きの変更が発生したときに発生するイベント、例えば invoice.paid または push.
  • ペイロード. codeが必要とする詳細が含まれたリクエストボディ。

The provider sends facts about something that already happened. Your job is to verify the sender, confirm the request is fresh, and apply the change once. That last part matters more than many basic tutorials admit. In production, duplicate delivery is normal behavior, not a corner case.

Practical rule: Webhookを使用することで、イベント駆動型の更新を実現できます。Pollingを使用することで、予定された読み取り、バックフィル、または出発イベントを提供しないプロバイダーに対して、スケジュールされた読み取りを実現できます。

チームがより広範な ワークフロー自動化とデータ統合を構築している場合、Webhookは通常、システムを同期させるために必要なリクエストトラフィックを無駄にしないイベントレイヤーになります。統合重視のサービスに従事している場合、Capgoの バックエンド開発記事 は、リトライ、キュー、観察性、エラー処理などのコア問題がリトライ、キュー、観察性、エラー処理などのコア問題が現れる場所です。

生産環境で機能するものと機能しないもの

通常、機能するセットアップは、設計上、面白みのないものです。必要なイベントのみにサブスクライブすること。プロバイダーまたはイベントファミリーに基づいてエンドポイントをスコープすること。イベントIDを保存することで、重複配信が繰り返されることなく、副作用を防ぎます。リクエストが検証され、キューに登録されたら、迅速に2xxのレスポンスを返し、次に、遅いビジネスロジックを非同期で実行します。

The fragile version is easy to recognize. One generic endpoint handles everything. Signature checks get skipped during early testing and never come back. The handler writes directly to critical tables before checking whether the event is authentic or stale. That works in a demo and fails under retry storms, provider outages, or an attacker replaying old requests.

That trade-off defines the rest of this guide. The “hello world” version of a webhook receiver is small. The production-ready version adds signature verification, replay defense, duplicate handling, and debugging hooks from the start.

WebhookのHTTPリクエストの構造

Before writing code, it helps to look at the request as raw HTTP instead of as a framework object. A typical webhook is just an HTTP POST to a public endpoint with headers and a JSON body.

A simple raw request

POST /webhooks/orders HTTP/1.1
Host: your-app.example
Content-Type: application/json
User-Agent: Provider-Webhooks/1.0
X-Webhook-Signature: sha256=abc123example
X-Webhook-Timestamp: 1712345678

{
  "event": "order.created",
  "id": "evt_123",
  "data": {
    "order_id": "ord_456",
    "status": "created"
  }
}

重要な部分は簡単です:

  • Method. 実際のウェブホック配信は通常POSTリクエストです。
  • Content-Type. 最近の多くのプロバイダーはJSONを送信しています。
  • User-Agent. デバッグに役立つですが、信頼できるものではありません。
  • 署名ヘッダー. プロバイダーの有効性確認を実行します。
  • タイムスタンプヘッダー. 古いまたは再送信された要求を拒否します。

ボディーの形がなぜ重要か

あなたのcodeは、通常、すべてのフィールドに気にしません。 イベントのタイプ、イベントの識別子、ビジネス オブジェクトが重要です。 data. それがなぜ、良いハンドラーは、必要なものだけをパースし、トラブルシューティングのために残りのものをログに記録する必要があるからです。

OpenAPIは、このパターンを直接モデル化しています。 OpenAPI 3.1.0は、トップレベルオブジェクトを追加して、最初のクラスウェブフックサポートを提供しました。 それぞれのウェブフックは、Path Itemと同様に記述されますが、プロバイダによってトリガーされます。 Canonical例では、ウェブフックに webhooks オペレーション、JSON要求ボディ、受信を示す newPet レスポンスが含まれます。 post operation, a JSON request body, and a 200 response to indicate receipt, as shown in the OpenAPI webhook example.

独自の受信者またはプロバイダーの契約をドキュメント化している場合、強力な例は抽象的なスキーマの文章よりも役立ちます。私は、 SheetMergyのAPIドキュメントの例 を使用するのを好みます。なぜなら、それはリクエストの例、フィールドの説明、そして期待されるレスポンスがどのように組み合わさっているか明確に示すからです。

Webhookはトランスポート層では単純です。ほとんどのエラーはヘッダ、ボディのエンコード、または署名規則に関する誤った仮定によるものです。

Webhook署名の安全な検証方法

署名されたWebhookは1つの質問に答えます: そのペイロードは共有シークレットを知る誰かのものかどうか?

これは、リクエストが最近であるか、またはすでに処理したことがあるかどうかを尋ねることとは異なります。署名の検証は最初のゲートではなく、最後のゲートではありません。

Webhook署名の検証フローを示すインフォグラフィック

通常のHMACフローは次のようになります。

プロバイダーのヘッダから署名を読み取ります。

  1. 検証フロー
  2. を参照してください 受信したままのリクエストボディを読み取ってください。 安全な構成からWebhookシークレットを読み取ります。
  3. 同じアルゴリズムを使用して、再計算された期待値のHMACを取得します。
  4. タイミングセーフな比較を使用して、受信した署名と計算された署名を比較します。
  5. 一致しない場合は、リクエストを拒否します。
  6. 実際の__CAPGO_KEEP_0__で何をチェックするか

これらの間違いは、よく実装されている場合でも最もよく見られるものです。

What to watch for in real code

. これを行うことは避けるべきです

  • .. JSON.stringify(req.body) と期待するのは、等しいはずです。
  • 通常の文字列の等しさを使用します。. 時間安全の比較を使用します。
  • 機密情報をハードコードする. 環境変数またはシークレットマネージャーで保管してください。
  • ヘッダーだけに依存することはない. 署名ヘッダーは、検証しない限り意味がありません。

サービス間でシークレットの取り扱いを強化するチーム向けに、Capgoの「 APIのアプリストアの準拠性のためのシークレットのセキュリティガイド」 は関連性があります。シークレットのローテーション、スコープ付きアクセス、ログ内の漏洩を回避することは、ウェブフック受信者にとっても重要です。

一般的な検証例

const crypto = require('crypto');

function verifySignature(rawBody, receivedSignature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  const a = Buffer.from(receivedSignature, 'utf8');
  const b = Buffer.from(expected, 'utf8');

  if (a.length !== b.length) return false;
  return crypto.timingSafeEqual(a, b);
}

これは意図的に一般的です。実際のプロバイダーは、署名を接頭辞でプレフィックスする、タイムスタンプを署名内容に組み込む、またはハッシュを別の方法で符号化することがよくあります。ルールは同じです。プロバイダーの正確な署名形式に従い、常に元のペイロードと検証してください。

再送信攻撃を防ぐ

署名されたWebhookは、数時間後に到着し、ハンドラーがそれを新しいものとして扱う場合でも危険です。チームはそれを想定していません。プロキシはトラフィックを記録し、リクエストペイロードは間違った場所に流れ出します、またはプロバイダーはネットワーク障害後にリトライし、エンドポイントは同じイベントを2回処理します。

Webアプリケーションにおける再送信攻撃を効果的に防ぐための5つの重要なセキュリティ対策を示すチェックリスト。

署名検証は次の質問に答えます: 送信者は共有シークレットでこのペイロードを作成しましたか? 再送信保護は別の質問に答えます: このリクエストは今すぐまだ受け入れるべきですか? 製品受信者には両方が必要です。

実際に重要な最小限のチェック

署名されたタイムスタンプから始まる実用的再送信防御

プロバイダーはヘッダーまたは署名されたメッセージにタイムスタンプを含め、受信者は小さな許容範囲外のリクエストを拒否します。

  • このフローは次のようになります。プロバイダーが定義した場所からタイムスタンプを読み取ります。
  • ヘッダー名を推測しないでください。RFC形式の日付または整数として解釈します。
  • プロバイダーの仕様に基づいています。.
  • 古すぎるまたは将来のリクエストを拒否する.
  • 署名スキームの一部としてタイムスタンプを検証する 提供者が対応している場合。

最後の点は重要です。タイムスタンプが署名によってカバーされていない場合、攻撃者はオリジナルのボディに新しいタイムスタンプを挿入して再生できます。私は常に提供者の正確な署名形式を確認する前にタイムスタンプロジックを信頼する

許容範囲の選択肢

5分は一般的なデフォルトです。攻撃ウィンドウを縮小する程度に短く、時刻のズレや通常のネットワーク遅延を乗り越える程度に長い

ここではトレードオフがあります。30秒のウィンドウはより安全に見えますが、実際のシステムではリトライ、キューング、地域遅延などが絡むと頻繁に破綻します。30分のウィンドウは操作が容易ですが、署名されたリクエストが漏洩した場合に攻撃者に与える時間が長くなります。最初は数分、サーバーをNTPと同期し、提供者の配信パターンが対応する場合にのみ厳密化してください。

リプレイ防御は単にタイムスタンプのチェックではありません

タイムスタンプの検証は古いリクエストをブロックしますが、有効なウィンドウ内で同じ署名されたイベントが 2 回送信された場合、同様のイベントを認識するにはアプリケーションは別のレイヤーを使用する必要があります。

2 番目のレイヤーを使用する

  • イベント ID または配信 ID を短期間のストレージである Redis にトラッキングする 短期間のストレージである Redis にイベント ID または配信 ID をトラッキングする
  • ハンドラを idempotent と見なす 重複した配信が発生した場合、重複した注文、メール、請求アクションを生成しないようにする
  • 古いリクエストを拒否した場合の理由コードを記録する シークレットやフルな敏感なペイロードを記録しない
  • 検証とキューの重い作業を別の場所で行った後、迅速なレスポンスを返す 既存のチームが有効期限のウィンドウと削除を考慮している場合、パターンを認識する。 __CAPGO_KEEP_0__の指南

Capgoアプリのトークン削除パターン Capacitor 有効期限切れの資格情報やリクエストは、有効期限切れの資格情報やリクエストは安全ではない。

Node.js で Webhook 受信者を作成する

Node と Express を使用すると、シリアスな受信者をオンラインにしたりするのが最も速い方法ですが、ある陷阱が他のすべてのものよりも重要です。

Express がオブジェクトに変換する前に、RAW ボディにアクセスする必要があります。

ノートパソコンが木の机の上に置かれ、Node.js 受信者 code を VS Code エディター環境で表示しています。

生産的な Express の例

const express = require('express');
const crypto = require('crypto');

const app = express();
const PORT = process.env.PORT || 3000;
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

// Capture raw body for signature verification
app.use(
  express.json({
    verify: (req, res, buf) => {
      req.rawBody = buf;
    },
  })
);

function safeEqual(a, b) {
  const aBuf = Buffer.from(a, 'utf8');
  const bBuf = Buffer.from(b, 'utf8');
  if (aBuf.length !== bBuf.length) return false;
  return crypto.timingSafeEqual(aBuf, bBuf);
}

function verifySignature(rawBody, secret, receivedSignature) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  return safeEqual(expected, receivedSignature);
}

function isFresh(timestampHeader, toleranceSeconds = 300) {
  const timestamp = Number(timestampHeader);
  if (!Number.isFinite(timestamp)) return false;

  const now = Math.floor(Date.now() / 1000);
  return Math.abs(now - timestamp) <= toleranceSeconds;
}

app.post('/webhooks/example', async (req, res) => {
  const signature = req.get('x-webhook-signature');
  const timestamp = req.get('x-webhook-timestamp');

  if (!WEBHOOK_SECRET) {
    return res.status(500).send('Webhook secret is not configured');
  }

  if (!signature || !timestamp) {
    return res.status(400).send('Missing required security headers');
  }

  if (!isFresh(timestamp)) {
    return res.status(401).send('Stale webhook');
  }

  const valid = verifySignature(req.rawBody, WEBHOOK_SECRET, signature);
  if (!valid) {
    return res.status(401).send('Invalid signature');
  }

  // Acknowledge quickly
  res.status(200).send('OK');

  // Process after acknowledgement
  try {
    const event = req.body;
    console.log('Accepted event:', event.event, event.id);
    // enqueueJob(event)
  } catch (err) {
    console.error('Post-ack processing failed:', err);
  }
});

app.listen(PORT, () => {
  console.log(`Webhook receiver listening on ${PORT}`);
});

この構造が持続する理由

ここではいくつかの選択肢は意図的に行われました:

  • ミドルウェア内でRaw body のキャプチャが行われます. これにより、ハッシュのために元のバイトが保存されます。
  • タイムスタンプはビジネスロジックよりも前にチェックされます. 時間が経っていても、仕事をする必要はありません。
  • ルートは 200 すぐに. 長時間実行される作業は、キューまたはバックグラウンドタスクに属するものです。
  • Post-ack 処理は分離されています. どんなに下流のロジックが失敗しても、受信パスは小さくなる。

シークレットは、多くのウェブホック実装の弱点です。ソースに保管しないでください、テスト用のフィクスチャに貼り付けてはいけません、ログに反映してはいけません。必要な場合は、CapgoのCI/CDパイプラインにおけるシークレットの管理のための「シークレットの回転とCIハンドリングのためのより広いプロセス」のガイドが、運用面をよくカバーしています。 動作を実際に確認するには、短いウォークスルーが役立ちます。 実際のシステムでは、変更したいこと

実際のプロバイダーの統合では、イベントIDの重複排除を永続ストレージに、リクエストID付きの構造化ログを、認証パスの後ろにキューを追加するなど、以下のような変更を加えたい。

単一の汎用エンドポイントを避け、複数のプロバイダーが異なる署名形式を使用する場合、分離されたハンドラーは論理的で、破壊されにくい。

PythonでWebhook受信者を作る

Flaskは、明確なリクエストハンドリングと、HMAC用のPython標準ライブラリが用意されているため、クリーンなWebhook例として適しています。

Nodeと同じことを覚えておくことは、主なことです。ハッシュ値を検証する際は、パースされたJSON辞書ではなく、元のリクエストバイトを使用することです。

署名とタイムスタンプのチェックを含むFlask例

Flask固有の詳細

import os
import time
import hmac
import hashlib
from flask import Flask, request, jsonify

app = Flask(__name__)
WEBHOOK_SECRET = os.environ.get("WEBHOOK_SECRET", "")

def is_fresh(timestamp_header, tolerance_seconds=300):
    try:
        timestamp = int(timestamp_header)
    except (TypeError, ValueError):
        return False

    now = int(time.time())
    return abs(now - timestamp) <= tolerance_seconds

def verify_signature(raw_body, secret, received_signature):
    expected = hmac.new(
        secret.encode("utf-8"),
        raw_body,
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(expected, received_signature)

@app.route("/webhooks/example", methods=["POST"])
def webhook():
    if not WEBHOOK_SECRET:
        return "Webhook secret is not configured", 500

    signature = request.headers.get("X-Webhook-Signature")
    timestamp = request.headers.get("X-Webhook-Timestamp")

    if not signature or not timestamp:
        return "Missing required security headers", 400

    if not is_fresh(timestamp):
        return "Stale webhook", 401

    raw_body = request.get_data()

    if not verify_signature(raw_body, WEBHOOK_SECRET, signature):
        return "Invalid signature", 401

    payload = request.get_json(silent=True) or {}

    # Acknowledge receipt
    response = jsonify({"status": "ok"})

    # In production, queue payload here instead of heavy sync work
    print("Accepted event:", payload.get("event"), payload.get("id"))

    return response, 200

if __name__ == "__main__":
    app.run(port=5000, debug=True)

Flaskの重要な詳細

request.get_data() はここでの重要な呼び出しです。 これは、ボディのrawバイトを提供します。 すぐに へジャンプすると、署名の不一致が混乱を招く点で線を越えていることになります。 request.json実装に関するいくつかの注意点があります:

を使用するのではなく、平等の代わりに使用してください。

  • ヘッダが欠落している場合、クライアントの失敗として扱い、早期に拒否してください。 hmac.compare_digest JSONのパースに使用する場合は、エラー処理を制御したい場合は、代わりに使用してください。
  • ルートを薄く保ちます。 . 付加的なコストを引き起こす可能性のあるペイロードがトリガーを発生させた場合、作業をキューに追加してください。 __CAPGO_KEEP_0__
  • __CAPGO_KEEP_0__ silent=True __CAPGO_KEEP_0__ __CAPGO_KEEP_0__
  • __CAPGO_KEEP_0____CAPGO_KEEP_0__

Don’t debug signature mismatches by relaxing security checks. Debug them by printing exactly what bytes you hashed and exactly what format the provider expects.

チームが通常で困る場所

一般的な失敗のパスは、手作りの JSON ボディでテストし、実際のプロバイダーに切り替えて、署名が一致しなくなったときです。その場合、通常は 3 つの理由のうち 1 つが原因です: プロバイダーがタイムスタンプ付きのエンベロープを署名した、署名が想定どおりにエンコードされていない、またはミドルウェアがボディを検証する前に変更した。

その場合、ランダムに暗号化 code を変更しないでください。ヘッダとボディのRAWデータをキャプチャし、ハッシュを再現するための小さな孤立したスクリプトを作成し、最後にFlaskルートに戻してください。

GoでWebhook受信者を作成する

GoはWebhook受信者としての素晴らしい選択肢です。標準ライブラリは十分で、フレームワークを必要とせずに小さく信頼できるハンドラーを取得できます。また、codeを簡単に正しく保つことができます。

注意するべきことはボディの処理です。 r.Body ボディはストリームです。1度だけ読み、取得したバイトをハッシュし、次に同じバイトからアンマーシャルしてください。

標準ライブラリの例

package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/hex"
	"encoding/json"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"time"
)

type WebhookPayload struct {
	Event string          `json:"event"`
	ID    string          `json:"id"`
	Data  json.RawMessage `json:"data"`
}

func isFresh(timestampHeader string, toleranceSeconds int64) bool {
	ts, err := strconv.ParseInt(timestampHeader, 10, 64)
	if err != nil {
		return false
	}

	now := time.Now().Unix()
	diff := now - ts
	if diff < 0 {
		diff = -diff
	}

	return diff <= toleranceSeconds
}

func verifySignature(rawBody []byte, secret string, received string) bool {
	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write(rawBody)
	expected := hex.EncodeToString(mac.Sum(nil))

	if len(expected) != len(received) {
		return false
	}

	return subtle.ConstantTimeCompare([]byte(expected), []byte(received)) == 1
}

func webhookHandler(secret string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
			return
		}

		signature := r.Header.Get("X-Webhook-Signature")
		timestamp := r.Header.Get("X-Webhook-Timestamp")

		if signature == "" || timestamp == "" {
			http.Error(w, "missing required security headers", http.StatusBadRequest)
			return
		}

		if !isFresh(timestamp, 300) {
			http.Error(w, "stale webhook", http.StatusUnauthorized)
			return
		}

		rawBody, err := io.ReadAll(r.Body)
		if err != nil {
			http.Error(w, "failed to read body", http.StatusBadRequest)
			return
		}

		if !verifySignature(rawBody, secret, signature) {
			http.Error(w, "invalid signature", http.StatusUnauthorized)
			return
		}

		var payload WebhookPayload
		if err := json.Unmarshal(rawBody, &payload); err != nil {
			http.Error(w, "invalid json", http.StatusBadRequest)
			return
		}

		w.WriteHeader(http.StatusOK)
		w.Write([]byte("OK"))

		log.Printf("accepted event=%s id=%s", payload.Event, payload.ID)
	}
}

func main() {
	secret := os.Getenv("WEBHOOK_SECRET")
	if secret == "" {
		log.Fatal("WEBHOOK_SECRET is not set")
	}

	http.HandleFunc("/webhooks/example", webhookHandler(secret))

	log.Println("listening on :8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Goがここで堅い理由

いくつかの利点が目立っています。

  • ハンドラーは明確です. No hidden middleware magic.
  • エッジでタイプが役に立つ. ヘッダーパース、タイムスタンプの変換、JSONのデコードがすべて明確に失敗します。
  • 標準の暗号化パッケージが十分です. 基本的なHMAC検証のために追加の依存関係が必要ありません。

運用メモ

ウェブフックの量が増えると、Goの並行性モデルはHTTPエントリポイントを変更することなくバックグラウンドワークを拡散させるためのスペースを与えます。そうしても、受信者は狭くしてください。受信、検証、承認、そして応答を返す前に、タスクを移譲してください。

Goのウェブフックハンドラーの中で最も強力なものは、常に面白くありません。トランスポート検証とビジネスロジックを混ぜることはなく、応答が戻る前にデータベースの重い作業を行わないのです。

基本的なデバッグテクニック

ウェブフックのバグは通常、スタックトレースではなくサポートメッセージとして表示されます。プロバイダーはイベントを配信したと言い、エンドポイントは何もアプリに到達しない、または最初から見た目が正常に見えるリクエストで署名検証が失敗したと言います。その時点で、デバッグは、バイトごとに正確なHTTP交換を再構築し、どこで破綻したかを証明することです。

ウェブフックをデバッグするための5つの基本ツールとテクニックのリスト

ウェブフックをデバッグするための実用的なツールキット

__CAPGO_KEEP_0__。

署名検証が失敗した場合、元の受信元のヘッダーと共に、元の受信元の本体を正確にキャプチャします。実際には、問題はよくありません。フレームワークはJSONをハッシュする前にパースしたり、プロキシがエンコードを変更したり、テスト再生では元のタイムスタンプヘッダーを無視したりします。パースされたオブジェクトをログするだけでは十分ではありません。元のバイトと検証入力が必要です。

これらのツールは、問題を迅速に分離するのに役立ちます:

  • リクエストのキャプチャ. 検証中のヘッダー、コンテンツタイプ、コンテンツ長、変更されていない本体をログします。
  • リクエストの検査エンドポイント. 送信元が送信したものを確認するのに役立つサービスがあります。 webhook.site ローカル トンネリング
  • と同様のツールは、プロバイダーをループさせながら、ローカル受信元に対してテストできます。. ngrok 手動再生
  • . リクエストを元に再構築します。Start with the wire format. curl または、Postmanを使用して同じボディとヘッダーで確認することができます。 これが、codeまたはプロバイダーのペイロードが問題であるかを確認する最速の方法です。
  • プロバイダーの配信ログ送信者ダッシュボードでは、レスポンスコード、リトライ履歴、リクエスト識別子が含まれており、ログと照合することができます。

パターンは重要です。外から内へ作業してください。最初に、プロバイダーが期待どおりに送信したかどうかを確認してください。次に、サーバーが同じバイトを受け取ったかどうかを確認してください。最後に、codeが同じバイトを、同じシークレットとタイムスタンプのルールでハッシュしたかどうかを確認してください。

実際に役立つログ

良いウェブホックログは、1つの検索で3つの質問に答えるべきです。

質問 役立つログフィールド
リクエストが到着したか? ルート、メソッド、受信日時
なぜ却下された? 欠落ヘッダー、古いタイムスタンプ、署名失敗
後で関連付けられるか? __CAPGO_KEEP_0__

実システムでは、4 番目のフィールドが役に立つ request_id ローカルに追加して、受信者の生成物を保存して、リクエストをアプリ、キュー、ワーカーログを通して追跡できるようにしてください。

保存するものを選んでください。秘密をログに記録しないでください。顧客データ、アクセストークン、請求情報を含む全プロダクションペイロードをダンプしないでください。代わりに、メタデータと短いボディハッシュをログに記録してください。リトライを比較し、2 つの配信が同じかどうかを確認するには、それでも十分です。

元の入力を使用して、失敗を再現する

基本的なチュートリアルはこの部分を省略します。失敗したリクエストを完全に再生できない場合は、推測していることになります。

失敗したウェブフックを保存する

  • 元のボディバイト
  • すべての署名関連ヘッダー
  • リクエストタイムスタンプ
  • コンテンツタイプ
  • プロバイダー要求 ID

次に、ステージングエンドポイントに再生してみましょう。再生が成功した場合、転送中の変更を比較してみましょう。一般的な原因としては、リクエストボディを正規化するミドルウェア、文字エンコードの不一致、ヘッダを削除または書き換えるロードバランサなどが挙げられます。実際のリクエストボディではなく、ダッシュボードのプレTTYプリントされたビューからペイロードをコピーしたチームによる失敗も見たことがあります。ホワイトスペースの差異 alone でHMAC検証を破ることができました。

より広範なリリースとモバイルトランスポートのトラブルシューティングのための、同じデバッグの規範がCapgoのガイドに現れます。 OTA更新のデバッグに使用するツールについてのCapacitor異なるトランスポートでも、同じ教訓が現れます。実際のリクエストパスをキャプチャする前に、codeを変更しないでください。

署名検証が失敗した場合、raw bytes、検証に使用したexactヘッダ、タイムスタンプ値を確認することから始めてください。暗号化codeに触れる前に。

生産用Webhookのチェックリスト

生産用Webhookハンドラーは、ステージングで正常に動作しているように見えますが、最初のリトライの嵐、不正なペイロード、2時ごろの署名不一致などで、いつのまにか問題が生じます。生産用のバーは高くなります。受信者は、偽造された要求を拒否し、有効なリトライを受け入れ、失敗のデバッグを行うためにオペレータに十分な信号を与えながら、敏感なデータを公開しないようにする必要があります。

セキュリティと正確性のチェック

  • すべての要求署名を検証するエンドポイントURLは漏洩します。テストURLはチャットで共有されます。署名検証は、共有シークレットを知っている送信者がいることを示す制御です。
  • 古い要求を拒否する. 旧のペイロードに有効な署名は、再送信されることができます。 供給元のリトライモデルに合致するタイムスタンプの許容範囲を強制します。
  • Hash の対象は、パースされた JSON ではなく、raw body です. ミドルウェアは、キーを並べ替える、ホワイトスペースを正規化する、またはエンコードを変更することができます。 検証は、到着した exact バイトに実行する必要があります。
  • code から署名シークレットを保管してください. 環境変数は基本的なものです。 ご自身の資格情報を定期的に回転する、または複数の環境で実行する場合は、シークレットマネージャーがより適切な選択になります。
  • 認証エラーの場合、失敗する. 署名ヘッダが欠落している、不正な、または予想外のスキームを使用している場合、リクエストを拒否し、理由をログに記録します。

信頼性チェック

  • 速いことを認めます. 供給元は通常、2xx のすべてを成功として扱います。 したがって、リクエストを検証し、必要なものを保存し、後処理をキューまたはワーカーに移す必要があります。
  • ハンドラーを idempotent にします. 同じイベントは複数回到着する可能性があります。 イベント ID、配信 ID、または安定した提供元識別子を使用して、イベントの副作用を分離します。
  • 予測可能なエラーコードを返します。. 不正入力または検証失敗の場合に使用し、システムが問題である場合にのみ使用すると、プロバイダーのリトライ動作を推論しやすくなります。 400 パースする前に、制限を設定します。 401 . webhook エンドポイントが一般的なインジェクションホールに変化するのを防ぐために、リクエストサイズ、コンテンツタイプ、ヘッダーの数を早期に設定します。 403 契約を狭く維持します。 5xx . サポートするフィールドとイベントタイプのみを受け入れるようにしてください。緩いパースは最初は便利ですが、プロバイダーの__CAPGO_KEEP_0__変更の際に高価になります。
  • 観察可能性のチェック正常なwebhook操作は面白くありません。チームは3つの質問に迅速に答えることができます: 受信したか? 検証したか? 末端処理が成功したか?
  • webhookエンドポイントが一般的なインジェクションホールに変化するのを防ぐために、リクエストサイズ、コンテンツタイプ、ヘッダーの数を早期に設定します。. Accept only the fields and event types you support. Loose parsing feels convenient at first and becomes expensive during provider API changes.

正常なwebhook操作は面白くありません。チームは3つの質問に迅速に答えることができます: 受信したか? 検証したか? 末端処理が成功したか?

システムが問題である場合にのみ使用すると、プロバイダーのリトライ動作を推論しやすくなります。

Use that standard:

  • 受信、検証、処理を個別の結果としてトラッキングする.
  • リクエストID、イベントID、署名ステータス、タイムスタンプのずれをログする.
  • キュー遅延、ハンドラー遅延、リトライの量を測定する.
  • ステージングまたは再送信ワークフロー用に安全な再送信パスを維持する.
  • パターン変更にアラートする署名失敗の増加や重複配信などのパターン変更にアラートする

Capgoは、更新ワークフローでリリース配信と観察性のツールを含み、Webhook関連フローの部分も触れている。実践的な教訓である。配信システムには、受信から完了までの可視性が必要である。

チームが上記のチェックをカバーしている場合、Webhook受信者は通常、生産用に良好な状態にある。チェックのいずれかが欠けている場合、その欠陥はデモ中に発生するのではなく、インシデント中に発生する。

Webhookに関するよくある質問

codeのステータスを何を返すべきか

ステータスを返す 200 __CAPGO_KEEP_0__ 400 __CAPGO_KEEP_0__ 401 __CAPGO_KEEP_0__

__CAPGO_KEEP_0__

__CAPGO_KEEP_0__

__CAPGO_KEEP_0__

__CAPGO_KEEP_0__

__CAPGO_KEEP_0__

__CAPGO_KEEP_0__

__CAPGO_KEEP_0__

__CAPGO_KEEP_0__


If your team ships Capacitor or Electron apps, Capgo は、関連する理由で知る価値がある。チームに、署名されたWeb更新を配信するための制御された方法、ロールアウトの動作を観察する方法、アプリストアのレビューを待たずにインシデントから回復する方法を提供します。これは、Solid Webhook設計の背後にあるエンジニアリングの本能に合致しています: 入力値を検証する、リリースパスの観察性を維持する、回復を高速化する。

Capacitor アプリのリアルタイム更新

ウェブ層のバグが生じた場合、Capgo を通じて修正を配信し、数日間待つ必要のないアプリストアの承認の待ち時間を避けることができます。ユーザーはバックグラウンドで更新を受け取り、ネイティブの変更は通常のレビューのパスを通ることができます。

今すぐ始めましょう

ブログの最新記事

Capgoは、プロフェッショナルなモバイルアプリを作成するために必要な最良の洞察を提供します。