← 全記事
比較

Telegram Webhookのsecret_tokenとHMAC:受信側で何を検証するか

Telegram Bot APIのsecret_tokenは、HMAC署名ではありませんsetWebhookで設定すると、Telegramは同じ値をX-Telegram-Bot-Api-Secret-Tokenヘッダーに入れて送信します。受信側は設定済みのトークンと照合します。一方、署名を有効にしたUnifyPortのwebhookでは、タイムスタンプとリクエスト本文の生バイト列からHMAC-SHA256を計算し、X-Device-Signatureを検証します。この2つの手順は交換できません。

最初に押さえる違い

  • Telegramのヘッダーは共有認証情報そのものです。JSON本文から計算したダイジェストではありません。
  • UnifyPortの署名は、エンドポイントのsigning_secretを使い、生の本文とX-Device-Timestampを結び付けます。
  • どちらにもHTTPS、秘密情報の保護、重複処理を防ぐ設計が必要です。
  • 検証方式は信頼できるルート設定で決めます。未検証のJSON内のproviderや、たまたま付いているヘッダーから選んではいけません。

この記事の対象はリクエスト認証です。ボットと既存のメッセージングアカウントのどちらを使うかについては、Telegram Bot API webhookと統一受信webhookの比較を参照してください。

Telegramのsecret_tokenが検証するもの

Telegram公式Bot APIリファレンスでは、secret_tokensetWebhookの任意パラメーターです。長さは1~256文字で、使用できる文字はA-Za-z0-9_-です。設定すると、各webhookリクエストのX-Telegram-Bot-Api-Secret-Tokenにその値が含まれます。

この保護を必須にする受信側では、ヘッダーが欠落している、または一致しないリクエストを信頼済みの処理キューに入れません。一致から分かるのは、設定した値をリクエストの送信者が持っていることです。その値と本文との間に、独立した暗号学的な結び付きができるわけではありません。

この違いは、プロキシや転送サービスで重要になります。トークンを読めるコンポーネントは、同じトークンを別の本文に付けて送信できます。HTTPSは通信接続を保護しますが、固定値のヘッダー自体はTLS終端後の本文変更を検出しません。これは信頼境界の違いであり、公式Bot APIを使わない理由ではありません。

webhookには専用の認証情報を用意し、bot tokenを流用しないことを推奨します。公開のリクエスト収集サービスに貼り付けず、アクセスログ、トレースのエクスポート、サポート用の画面キャプチャにも残さないようにします。

固定トークンと本文署名の比較

HMACは、共有秘密鍵を使ったメッセージ認証方式です。UnifyPortの契約では、ヘッダーと秘密鍵を直接比較するのではなく、指定された入力からダイジェストを再計算します。

項目Telegram Bot API webhook署名付きUnifyPort webhook
設定setWebhook.secret_tokenエンドポイントのsigning_secret
検証するヘッダーX-Telegram-Bot-Api-Secret-TokenX-Device-Signature
受け取る値設定したトークン自体16進数のHMAC-SHA256ダイジェスト
本文を検証対象に含むか含まない生のバイト列を含む
タイムスタンプを含むか含まないX-Device-Timestampを含む
受信側の処理ヘッダーとトークンを照合ダイジェストを再計算して照合
一度だけの処理を保証するかしないしない

UnifyPortの署名対象は厳密に次の形です。

<X-Device-Timestamp>.<raw request body>

タイムスタンプはRFC 3339 UTC形式で、区切りは文字としてのピリオドです。JSONの整形、空白の変更、解析後の再シリアライズは、署名対象のバイト列を変える可能性があります。signing_secretはHMACの鍵であって、署名ヘッダーにそのまま入る値ではありません。

signing_secretが空の場合、UnifyPortの署名は無効になり、X-Device-Signatureは送られません。署名を必須にする受信側は、認証方式を自動で切り替えずに拒否する必要があります。

受信ルートを分離する

ネイティブのTelegram更新とUnifyPortイベントには、別々のアプリケーションルートを用意する設計を推奨します。これはアプリケーション側の推奨設計であり、新しいプロバイダーAPIではありません。LINEとTelegramを同じサポートキューに集約する場合も、認証の境界は先に分けておきます。

  1. ルートと送信元・認証情報を結び付ける。 デプロイ設定で検証方式を固定します。
  2. 配送処理の前に認証する。 Telegramはトークンヘッダー、署名付きUnifyPortは生の本文のHMACとタイムスタンプの鮮度を確認します。
  3. それぞれの形式を検証する。 TelegramのUpdateを、UnifyPortのmessage.receivedを期待するハンドラーに渡しません。
  4. 受け付けた仕事を永続化する。 認証、冪等性、業務上の権限確認は別々に扱います。
  5. 秘密ではなく失敗の種類を記録する。 どの検証に失敗したかだけをログに残します。

共通ルートで「どちらかのヘッダーが通れば受け付ける」ミドルウェアは避けてください。弱い分岐や誤って有効にした分岐が、本来の検証ポリシーに代わる入口になります。特にUnifyPortルートで必要なHMACを、Telegramのトークンで代用してはいけません。

実装の詳細はHMACのリプレイ対策と再試行処理を参照してください。メッセージJSON内の時刻は、配信署名で保護されたタイムスタンプの代わりにはなりません。

本番投入前の受け入れテスト

以下は推奨テストであり、実行済みの結果ではありません。管理下の認証情報を使い、隔離環境で確認してください。

テスト期待する挙動
Telegramヘッダーが欠落または不一致信頼済み処理の前に拒否
Telegramトークンは正しいが本文が変更されたヘッダー検証だけでは検出できない。形式検証と信頼できる通信境界が必要
UnifyPort本文が署名後に変更されたダイジェスト不一致で拒否
UnifyPort署名は正しいが時刻が許容範囲外鮮度ポリシーに従って拒否
UnifyPortルートにTelegramトークンを送る検証方式を変えずに拒否
正当な通常イベントが再配信される業務処理を繰り返さず、冪等に受け付ける

時刻の許容範囲は、時計の精度と配信条件に合わせて設定します。別サービスの値をそのまま使わないでください。また、認証が成功しても、メッセージ内のすべての命令を実行してよいとは限りません。

UnifyPortの役割と限界

UnifyPortの非公式インターフェースは、接続済みのメッセージングアカウントから正規化イベントを提供します。HMACが保護するのは、UnifyPortから受信サーバーへの受け渡しです。Telegramネイティブの署名でも、Telegramユーザーによる作成をエンドツーエンドで証明するものでもありません。

Telegramボットとして動く製品なら、その入口で公式の保護を実装すれば十分です。認証方式を変えるためだけに移行する必要はありません。UnifyPortでアカウント単位または複数チャネルのキューを構築する場合は、独自の契約に従い、webhookエンドポイントの作成時に署名を有効にしてください。

よくある質問

Telegramのsecret_tokenでHMACを計算しますか?

公式Bot APIの秘密ヘッダーを検証するためには計算しません。ヘッダーを設定済みのトークンと比較します。トークン自体を運ぶヘッダーに、独自の本文署名アルゴリズムを想定しないでください。

X-Device-Signatureとsigning_secretを直接比較できますか?

できません。文書で指定されたタイムスタンプと生の本文からHMAC-SHA256を再計算し、受け取った16進数の署名と比較します。

どちらかを使えば重複処理を防げますか?

いいえ。認証と重複排除は別です。受け付けた仕事を永続化し、後続の操作も冪等にしてください。HMACだけで一度きりの配信にはなりません。

次のステップと出典

UnifyPort受信側の実装契約には、webhook配信と署名検証リファレンスを使用してください。

出典確認日:2026-09-17。

UnifyPort API

メッセージ連携を安定したプロダクトパイプラインへ。

まずは 1 つの API で送信を始め、標準イベントですべての inbound メッセージを業務システムへ戻しましょう。