https://s3.amazonaws.com/a.storyblok.com/f/270183/104214/35c943c296/social_validate-inbound-messages_1200x627.png

Vonage Messages API からの受信メッセージの検証

最終更新日 May 5, 2021

所要時間:1 分

ウェブフック入門

Vonage Messages API Vonage Messages APIは驚異的だ。アドボケイトとして贔屓目に見てはいけないことは分かっているが、実はVonage APIの中で一番気に入っている。これによって 送信WhatsApp、Facebook Messenger、Viber、SMS、MMSを使ったメッセージの送信がとても簡単にできます。そしてもう一方では 受信SMSメッセージはアカウントレベルのエンドポイントに送り返されます)。

アプリケーションがVonageからインバウンドメッセージ(またはアウトバウンドメッセージのステータス)を受信するには、アプリケーションにVonageがメッセージを送信できるパブリックにアクセス可能なHTTPエンドポイントが必要です。このメッセージはWebhookと呼ばれます。セキュリティとWebhookに関する質問が多いので、MessagesのWebhookをセキュアにする方法を紹介します。ベアラ認証とペイロード検証の組み合わせでこれを実現します。

Webhookのセキュリティに関する懸念

Webhookにまつわる最大の疑問は、Webhookが何であるかを超えて、悪意のあるWebhookを悪質な業者が送信しないように、Webhookをどのように保護するかということです。もし攻撃者がウェブフックのエンドポイントにアクセスし、ユーザーデータを大量に取得した場合、何ができるのでしょうか?それは妥当な懸念だ。

こう考えてみよう:アリスはアプリケーション開発者で、ボブからWhatsAppメッセージを受け取る必要がある。ボブはアリスのアプリケーションにWhatsAppメッセージを送る。WhatsAppメッセージを受信すると、VonageはアリスのHTTPエンドポイントにメッセージを送り、彼女のアプリケーションに受信メッセージを通知する。そのエンドポイントは公開されていなければならない。悪意のあるハッカーであるチャックがアリスのウェブフックエンドポイントを見つけると、ボブになりすましてメッセージを受信したり、アウトバウンドメッセージのステータスを改ざんしたりすることができます。

インバウンド・ウェブフックの認証

Vonageは以下を使用します。 JSONウェブトークン(JWT)ベアラ認証を使用して、Messages API から送信される Webhook を簡単に認証できるようにしています。ベアラートークンはHMAC-SHA256トークンです。つまり、JWTの有効性を検証するのは、トークンをその署名秘密でデコードするのと同じくらい簡単です。あなたが使用する必要がある秘密は、ダッシュボードの設定ページに表示されているのと同じ秘密です。 ダッシュボードの.ブルートフォース攻撃を困難にするために、この署名秘密は少なくとも32ビットであることが推奨されます。アカウントの署名秘密はあなたとVonageの間で共有される秘密です。

signing secretsigning secret

インバウンド・ウェブフックのペイロードを検証する

トークンを認可することに加えて、Webhookのペイロードがトークンの言うものと一致していることをチェックするのは良い考えだ。JWTはデコードされると、独自のJSONペイロードを持ち、このJSONのフィールドはクレームと呼ばれます。攻撃者がトークンを盗んで再生する可能性を防ぐために、これらのクレームの1つである payload_hash.クレーム payload_hashは、Webhook のペイロードの SHA-256 ハッシュです。受信メッセージのペイロードを SHA-256 ハッシュにかけて、Webhook の claim と比較します。 payload_hashと比較することで、受信したトークンがリプレイでないことを確認できます。

トークンが生成された時間をチェックする

もう一つの重要な主張は iat-これは "issued at "の略で、トークンが生成されたUTC Unixタイムスタンプである。この iatを現在のUTC Unixタイムスタンプと比較して、そのタイムスタンプがどれくらい古いかをチェックすることで、トークンが古くなる可能性を防ぐことができる。

コードに翻訳する

これらのコンセプトをコードに変換してみよう。Node.jsでこれを行う方法を紹介するが、これらのテクニックはほぼすべてのプログラミング言語で利用可能だ。

依存関係の収集

という新しいディレクトリを作成する。 signed_webhooksそして cdという新しいディレクトリを作成する。このディレクトリで npm install dotenv jsonwebtoken js-sha256 express body-parser.

ファイルの作成と設定の追加

私たちの signed_webhooksディレクトリに server.js.envファイルを作成します。ファイル server.jsファイルにはサーバーのコードを書き、そして .envファイルには設定を置きます。ファイルには .envファイルにフィールドを1つだけ追加する、 NEXMO_API_SIGNATURE_SECRETというフィールドを追加し、そのフィールドに ダッシュボード設定ページにあります。

依存関係の初期化

さて、すべての依存関係を集めてサーバーを設定したので、サーバー・コードを追加する必要がある。まずは依存関係を初期化することから始めよう。server.jsに以下を追加します:

require('dotenv').config();
const jwt = require("jsonwebtoken");
const sha256 = require('js-sha256');
const app = require('express')();
const bodyParser = require('body-parser');
const NEXMO_API_SIGNATURE_SECRET = process.env.NEXMO_API_SIGNATURE_SECRET;
if(!NEXMO_API_SIGNATURE_SECRET){
    throw "Missing Signature Secret";
}

このコードは、すべての依存関係を取り込み、署名の秘密を環境から取り込む。

インバウンド・メッセージ・ルートの追加

次に、以下のルートを設定する必要がある。 inbound-messageそして status.のルートを設定する必要がある。 POSTリクエストであると仮定します。 /webhooks/inbound-message/webhooks/statusへのルートを追加し POSTリクエストを handleWebhook関数を経由するように設定します。

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
  extended: true
}))

app
    .route('/webhooks/inbound-message')
    .post(handleWebhook);
app
    .route('webhooks/status')
    .post(handleWebhook)

インバウンドメッセージの処理

インバウンドメッセージを受け取ったら、ボディを取り出し、ペイロードに追加する。それから、認可ヘッダーを空白で分割する(認可ヘッダーは "Bearer Token "という形式なので、空白で分割してトークン部分を取ればJWTが得られる)。

トークンを取得したら、署名の秘密を使ってJWTをデコードすることができる。先に説明したように、このデコード操作はトークンの真正性を検証することに等しい。JWTが有効でなかったり、authヘッダーが不正であったりすると、デコード操作によって例外が発生し、401が返される。トークンをデコードできた場合、トークンの真正性を検証したことになります。したがって、TLSを使用していてペイロードの検証を気にしない場合は、この時点で安全に200を返すことができます。ペイロードを検証する場合は、ペイロードに対して JSON.stringifyを実行し payload_hashと比較するだけである。改ざんを検出した場合は、エンドポイントから401を返し、認証されていないことを伝えることができる。最後に、指定したポートまたはポート3000でリッスンするようにアプリに指示する。

これらはすべて、次のようにして実現される。 handleWebhookリクエスト

function handleWebhook(request, response){
    const payload = Object.assign(request.query, request.body)
    try{
        let token = request.headers.authorization.split(" ")[1]
        var decoded = jwt.verify(token, NEXMO_API_SIGNATURE_SECRET, {algorithms:['HS256']});
        if(sha256(JSON.stringify(payload))!=decoded["payload_hash"]){
            console.log("tampering detected");
            response.status(401).send();
        }
        else{
            console.log("Success");
            response.status(204).send();
        }
    }
    catch(err){
        console.log('Bad token detected')
        response.status(401).send()
    }
}
app.listen(process.env.PORT || 3000)

テスト

テスト目的のため、ローカルで実行することにする。サーバーの起動は簡単だ。 node server.jsを実行すればサーバーが起動する。

ngrokのセットアップ

ウェブフックをサーバーにルーティングするために、ngrokを使おう。ngrokを使えば、ローカル・サーバー用のトンネルを作ることができる。以下のコマンドを実行する。

ngrok http 3000

そうすると、次のような ngrok セッションが表示されます。 http://random.ngrok.io-スワップアウト randomのようなngrokセッションが表示されます。 /webhooks/inbound-messageを追加すると、Webhooks の URL が出来上がります。

ngrokngrok

ウェブフックの設定

サーバへのトンネルができたので、メッセージを受信する前に最後にすることは、Webhook の設定です。テスト目的であれば、Messages APIのサンドボックスを使うとよいだろう。 Martynのブログ記事または メッセージAPIサンドボックスのドキュメント.

本番環境では、アプリケーションのウェブフックを設定する必要があります。これは ${CUSTOMER_DASHBOARD_URL}/applications/:appid/editで、:appid をアプリケーション ID に置き換えてください。Nexmo CLI または アプリケーションAPI.

編集後、アプリケーションのメッセージ用ウェブフック設定は以下のようになるはずです:

application webhooksapplication webhooks

すべての設定が完了したので、WhatsApp、Viber、Facebook Messengerのいずれかの番号にメッセージをテスト送信します!

インバウンドSMSの検証

インバウンドSMSの検証はこの記事の範囲外ですが、インバウンドSMSメッセージを検証するための同様の方法があります。その方法については 開発者向けドキュメント.

リソース

  • この記事のコードは GitHub.

  • JWT認証がどのように機能するかについてのより詳細な説明は、私たちの 開発者向けドキュメント.

  • 手動でデコードしたいJWTがある場合、次のようにして簡単にデコードできます。 jwt.ioのデコーダーを使えば簡単にできます。

シェア:

https://a.storyblok.com/f/270183/384x384/73d57fd8eb/stevelorello.png
Steve Lorelloヴォネージの卒業生

スティーブは、かつてVonageチームの一員でした。彼はVonageで.NET開発者アドボケートを務め、フルスタックかつ多言語対応のソフトウェアエンジニアであり、AI/MLの専門家でもありました。