【ITニュース解説】Validate Telegram Mini App Raw initData in Express to Stop Front-End Identity Spoofing in React
2026年11月05日に「Dev.to」が公開したITニュース「Validate Telegram Mini App Raw initData in Express to Stop Front-End Identity Spoofing in React」について初心者にもわかりやすく解説しています。
ITニュース概要
Telegram Mini AppをReactで開発する際、安易にユーザーIDを使うと「なりすまし」される危険がある。これを防ぐには、生の`initData`をExpressバックエンドに送り、Bot TokenでHMAC-SHA256署名を検証し、安全な認証を確立することが重要だ。
ITニュース解説
Telegram Mini Appは、人気メッセージングアプリであるTelegram内で動作する小さなウェブアプリケーションである。Reactのようなフロントエンドフレームワークを用いて開発され、ユーザーはTelegramアプリを離れることなく、注文システムやゲームなどの機能を利用できる。Telegramクライアントは、Mini Appが動作するウェブビューにwindow.Telegram.WebAppという特別なオブジェクトを挿入し、これにより開発者はTelegramの機能と連携できる。
多くの開発者は、ユーザーのプロフィール情報を取得するためにwindow.Telegram.WebApp.initDataUnsafe.userというプロパティを参照しがちである。このinitDataUnsafeは、ユーザーIDなどの情報を含むJavaScriptオブジェクトであり、クライアント側のブラウザによって直接パースされて利用される。しかし、Mini Appは標準的なウェブアプリケーションであり、ユーザーはデスクトップブラウザでMini AppのURLを開き、開発者ツールを使ってこのinitDataUnsafeオブジェクトの内容を簡単に改ざんできるという重大なセキュリティ上の脆弱性がある。例えば、攻撃者はこのオブジェクト内のユーザーIDを他のユーザーのIDに変更することで、そのユーザーになりすまし、管理者権限を悪用したり、他ユーザーの残高を不正に引き出したりすることが可能となる。このような身元詐称(Identity Spoofing)の脅威からアプリケーションを保護することは、開発において極めて重要である。
この脆弱性を解消するためには、initDataUnsafeを認証に利用してはならず、代わりにwindow.Telegram.WebApp.initDataという生のクエリ文字列を未検証のクレデンシャルとして扱い、これをサーバーサイドで厳格に検証する必要がある。initDataは、TelegramがMini Appを起動する際に生成する、URLエンコードされたパラメータの文字列である。この文字列には、ユーザー情報、認証日時、そして特に重要なものとしてHMAC-SHA256という暗号化された署名(ハッシュ)が含まれている。このハッシュは、Telegramのボットトークンから派生した秘密鍵を使ってTelegramによって計算されるため、フロントエンドでinitDataの値を少しでも変更すると、ハッシュが一致しなくなり、不正な改ざんが検知できる仕組みになっている。
ReactでMini Appを開発する際、まずHTMLのheadセクションに公式のTelegram WebAppスクリプトを読み込むことが推奨される。これにより、window.Telegram.WebAppオブジェクトが利用可能となる。フロントエンドアプリケーションは、初期ロード時にこのrawInitData文字列を、例えばAuthorizationヘッダーのようなHTTPヘッダーに含めて、バックエンドのAPIサーバーへ送信しなければならない。具体的には、TelegramInitData <initData>という形式で送信する。
バックエンドでは、この受信したrawInitDataがTelegramから送られた正当なものであり、かつ改ざんされていないことを検証する必要がある。この検証プロセスは、Telegram Bot APIの公式ドキュメントで定義されているHMAC-SHA256検証アルゴリズムに基づいて行われる。Expressなどのフレームワークでは、この検証ロジックを認証ミドルウェアとして実装できる。
検証アルゴリズムの具体的な手順は以下の通りである。まず、受信したURLエンコードされたinitData文字列をキーと値のペアにパースする。次に、このペアからhashキーとそれに対応する値を抽出して削除する。残りのすべてのキーと値のペアを、キー名のアルファベット順にソートする。ソートされたペアをkey=value\nkey=valueという形式で結合し、data_check_stringと呼ばれる文字列を生成する。
次に、Telegram Bot Token(これはサーバー環境変数として安全に保管され、クライアントには決して公開してはならない)を使い、定数文字列"WebAppData"をHMACキーとしてHMAC-SHA256ハッシュを計算し、シークレットキーを生成する。そして、この生成されたシークレットキーを用いて、先ほど作成したdata_check_stringのHMAC-SHA256ハッシュを計算する。最後に、この計算されたハッシュ(16進数形式)と、クライアントから受信したhashパラメータの値を比較する。この比較は、タイミング攻撃を防ぐために、Node.jsのcrypto.timingSafeEqualのような定数時間比較関数を使用して安全に行う必要がある。
さらに、リプレイ攻撃を防ぐために、initDataに含まれるauth_dateパラメータも検証する。auth_dateは認証が行われた日時を示すタイムスタンプであり、もしこの日付が現在の時間から24時間(86,400秒)以上経過している場合、そのリクエストは古いものとして拒否する必要がある。これにより、一度有効だったinitDataが悪意を持って再利用されることを防ぐ。検証がすべて成功した場合、サーバーはinitDataに含まれるユーザー情報を安全にパースし、リクエストオブジェクトに付加して、後続のAPI処理で利用できるようにする。
フロントエンドのReactアプリケーションでは、認証状態とUIの表示を適切に同期させる必要がある。useEffectフックを利用して、コンポーネントのマウント時にwindow.Telegram.WebApp.ready()を呼び出し、Telegramクライアントに対してアプリケーションがレンダリング準備完了であることを伝える。また、window.Telegram.WebApp.expand()を呼び出して、Mini Appのビューポートが最大の高さに拡張されるようにする。バックエンドでの認証が完了し、ユーザー情報が安全に取得されるまでは、ユーザーインターフェース(UI)の主要な要素やアクション(例えば、「支払い」ボタンなど)は無効化しておくべきである。
TelegramのMainButtonは、ReactのDOMツリーの外部にレンダリングされる特別なUI要素であり、window.Telegram.WebApp.MainButtonオブジェクトを通じて命令的に制御される。このため、MainButtonのイベントハンドラの管理には注意が必要である。コンポーネントがアンマウントされる際や、状態が更新されてuseEffectが再実行される際には、必ずMainButton.offClick(handler)を呼び出して、以前登録したイベントリスナーを解除しなければならない。これにより、複数のイベントハンドラーが重複して登録され、予期せぬ動作やメモリリークが発生することを防ぐ。
本番環境でTelegram Mini Appをデプロイする際には、いくつかの運用上の注意点がある。最も重要なのは、Bot TokenをReactクライアントサイドの環境変数として決して公開しないことである。HMACハッシュの生成は、必ずサーバーのメモリ内で安全に行われるべきである。auth_dateの検証ウィンドウは、リプレイアタックに対する保護として厳密に設定し、通常は24時間以内の有効期限とする。また、Telegram.WebApp.expand()は、iOSやAndroidのTelegramクライアント間で異なるモバイルウェブビューの表示を最適化するため、コンポーネントがマウントされた直後に呼び出すことが重要である。これらの対策を講じることで、セキュアで信頼性の高いTelegram Mini Appを構築できる。