Webhookのセキュリティ:その安全性は?

Momentoでリクエスト署名を使用して公開Webhookを保護します。

先日、Momento TopicsのWebhooksのリリースを発表しました。Webhooksは、リアルタイムモデレーションやチャットシステムの翻訳など、新しいワークフローを実現するMomento Topicsの強力な拡張機能です。Webhook は、新しいメッセージが公開されるたびに任意の POST API エンドポイントをトリガーできるようにすることで、より広範なイベント駆動型アーキテクチャ (EDA) に接続する強力な手段を提供します。これにより、ステップ関数のワークフローをトリガーしたり、Lambda関数を直接呼び出すなど、さまざまなワークフローを開始することができます。この柔軟性により、別の受信者に転送する前にメッセージの即時処理を実行したり、送信されたメッセージの後処理を追加するために非同期ワークフローを開始したりすることができます。

ウェブフックは安全か?

この機能は開発者にとって強力なツールですが、Webhookエンドポイントを構築する際に多くの開発者が最初に尋ねる質問の1つは、”このAPIで受信したメッセージが本当にMomentoから送信されたものであることを確認するにはどうすればよいのか “というものです。

ここでは、リクエスト署名と、それを使って受信したメッセージの送信者と完全性の両方を検証する方法について、深く掘り下げてみましょう。しかし、Momento のウェブフックとメッセージの検証方法に入る前に、まず一般的なリクエスト署名について説明する必要があります。

リクエスト署名は、メッセージやリクエストの信憑性と完全性を検証するた めにコンピュータシステムで使われるセキュリティ手段である。

リクエスト署名は、異なるコンポーネントやサービス間で通信が発生する ウェブアプリケーション、API、その他の分散システムで一般的に採用されています。これには、以下のようなセキュリティ上の利点があります:

認証: リクエストの送信者が本人であることを保証する。
完全性(Integrity): リクエストデータが送信中に変更されていないことを検証する。
否認防止: デジタル署名が発信元の証明となるため、送信者はリクエストの送信を 否認できない。

ウェブフックは一般的に公開エンドポイントであり、認証メカニズムによって保護されていないため、送信者が信頼できるソースであることを確認することは、システムの完全性にとって極めて重要です。

署名依頼の5つのステップ

Momentoは、Webhookに公開する前にすべてのリクエストに電子署名を付与するために、以下のプロセスを実装しています。

1.リクエストを作成する

ユーザーがリクエストを開始すると、送信者(Momento)は、リクエストメソッド、ヘッダー、ボディ、その他の関連する詳細などの情報を含むリクエストメッセージを生成します。

2.署名の作成

署名の作成次に、Momentoはリクエストデータに基づいてデジタル署名を作成します。この署名は、署名秘密とHMAC SHA3-256ハッシュを使用して生成されます。署名の秘密は、Webhookの作成時に提供されます。

3.リクエストに署名を追加する

生成された署名は、momento-signatureヘッダーに含まれるオリジナルリ クエストに付加されます。

4.署名されたリクエストを公開する

署名されたリクエストはウェブフックに送られます。

5.受信側での検証

リクエストを受信すると、受信者(あなたのコード)は以下のステップを実行します:
・リクエストデータと含まれている署名を取り出します。
・秘密鍵のコピーを使用して署名を再計算し、受信した署名と比較します。
・計算された署名が受信した署名と一致した場合、そのリクエストは真正 であり、転送中に改ざんされていないとみなされます。

リクエスト署名がどのようなもので、どのような利点があるのか、一般的な理解ができたところで、MomentoのWebhookでどのように実装されているのか、特にWebhookのセキュリティについて説明します。

作成された各ウェブフックには、固有の署名秘密が付与されます。この署名秘密を使って、このリクエストが Momento から来たものかどうかを確認することができます。Momento が送信する各 HTTP リクエストには、momento-signature ヘッダーが含まれます。この署名は、標準的な HMAC ハッシュを使用して署名秘密とリクエスト本文を組み合わせることで作成されます。

サインシークレットの取得

1.Momento SDKのputWebhook APIを使用して新しいWebhookを作成すると、レスポンスにsecretが返されます。

const result = await webhookClient.putWebhook(
'exampleCache', 'exampleWebhook', {
   destination: new PostUrlWebhookDestination('https://example.com/webhook'),
   topicName: 'exampleTopic',
});
if (result instanceof PutWebhook.Success) {
 console.log('Webhook created successfully. Secret:', result.secret);

2.getWebhookSecret APIを呼び出して、署名シークレットのコピーを取得することもできます。

const result = await webhookClient.getWebhookSecret(
	'exampleCache', 
	'exampleWebhook'
);
if (result instanceof GetWebhookSecret.Success) {
 console.log('Webhook secret retrieved successfully:', result.secret);
} 

インバウンドリクエストの検証:

1.リクエストからmomento-signatureヘッダーを取得する。

2.HMAC SHA3-256を使用して、ウェブフックに関連付けられた署名秘密を使ってリクエスト本体をハッシュ化する。

3.計算された署名をリクエストのmomento-signatureヘッダーと比較する。

import * as crypto from 'crypto';

const didRequestComeFromMomento = (req: Request): boolean => {
 const hash = crypto.createHmac("SHA3-256", "the signing secret");
 const hashed = hash.update(req.rawBody).digest('hex');
 return hashed === req.headers['momento-signature'];
}

また、Momentoはリクエストの検証を支援するユーティリティ関数を特定のSDKで提供しています。例えば、当社のJavaScript SDKを使用して、バリデーションを行うことができます:

import {WebhookUtils} from '@gomomento/sdk';

const res = WebhookUtils.validateWebhookRequest({
 body: requestBody,
 signature,
 signingSecret,
});
if (res === WebhookUtils.RequestValidation.VALID) {
   // request is valid
} else {
   // request is invalid
}

Webhookの宛先に署名検証を組み込むことで、受信したメッセージがMomentoからのみ発信されたものであることを確認できます。これは、メッセージの内容が添付された署名と一致していることを確認することで、確実性のレイヤーを追加します。リクエストを検証した後、自信を持ってメッセージの処理を進めることができます。これで、Momento からのメッセージであることが証明され、エンドポイントを詐称する悪意のある試みから保護され、Webhook のセキュリティが強化されました。

インバウンドのWebhookリクエストの検証については、開発者向けドキュメントをご覧ください。または webhooks の概要をご覧ください。