mcpField
T
telegram-miniapps

telegram-miniapps

github.com/ssheleg開発4153 回インストール4 日前に更新
リポジトリ

telegram-miniapps について

telegram-miniappsは、Telegram Mini Appの開発とセキュリティを扱うガイドスキルです。WebViewアプリの開発者向けに、署名付きinitDataの検証、端末のビューポートやセキュアストレージ、ボットへの結果返却を説明します。

主な機能

  1. 01HMACとEd25519署名の検証
  2. 02initDataの有効期間と重複項目の確認
  3. 03ビューポート、安全領域、テーマへの対応
  4. 04アプリ結果をボットに返す方法の説明
  5. 05実行可能なPython検証器の提供

活用シーン

  1. 01Telegram Mini Appのログイン検証実装
  2. 02initData検証とセッション交換のレビュー
  3. 03モバイルWebViewのレイアウト問題の修正
  4. 04Mini Appの結果をボットへ送信

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/ssheleg/telegram-dev --skill telegram-miniapps

インストール後、Claude Code で/telegram-miniappsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

fixtures/
verify_initdata.py
references/
app-to-bot.md
initdata-verification.md
viewport-and-platform.md

詳細

Telegram Mini Apps — 署名付きの単一データがすべてのモデルです

Mini App は、クエリ文字列が付いた WebView 内のウェブページです。そのクエリ文字列だけがユーザーの身元を証明するもので、ボットトークンで署名されています。 サーバーで検証すれば、ボット自体と同じ強度の認証が得られます。SDK が渡す解析済みオブジェクトを信頼すれば、認証は何も得られません。

2026-08-25 時点で core.telegram.org/bots/webapps を確認。Bot API 10.3。

詳細な資料は必要に応じて参照してください。

資料参照するタイミング
references/initdata-verification.md検証の実装またはレビュー時 — 失敗ケースも含め、2 つのアルゴリズムを 3 つの言語で解説します
references/app-to-bot.mdボットへの結果の返却、インラインクエリ、アプリ内での支払い
references/viewport-and-platform.md実機でアプリの表示がおかしいとき — ビューポート、安全領域、全画面表示、テーマ

実行可能なファイルが、このファイルとともに提供されています: fixtures/verify_initdata.py — そのまま使える検証コードと、改ざんされたユーザー ID、古い auth_date、再シリアライズされた user フィールド、逆順の鍵導出を拒否することを確認する 9 つのチェックが含まれています。python3 fixtures/verify_initdata.py の実行に必要なのは標準ライブラリだけです。


initDataUnsafe は意図的にその名前が付けられています

// The client hands you both. One is evidence, the other is a convenience.
const raw    = window.Telegram.WebApp.initData;        // the signed string — SEND THIS
const unsafe = window.Telegram.WebApp.initDataUnsafe;  // already parsed — display only

initDataUnsafe.user.id はユーザーが任意の値に設定できる数値です。ユーザーの端末上で実行されるページから取得されるためです。ユーザーの身元、所有物、閲覧できるものなど、何を決める場合も、サーバーで未加工の文字列を使って改めて判断する必要があります。 initData をバックエンドに送り、そこで検証したうえで、検証済みのデータからユーザーの身元を特定してください。

このスキルが防ぐ唯一の欠陥は、クライアントから { userId } を受け取るエンドポイントです。正常に動作し、デモでも使えますが、誰でも他人の情報を読めるようになってしまいます。

正確に検証する

import hashlib, hmac, time
from urllib.parse import parse_qsl

MAX_AGE = 300  # seconds; a decision, not a constant handed down

def verify(init_data: str, bot_token: str) -> dict:
    pairs = dict(parse_qsl(init_data, strict_parsing=True))   # already url-decoded
    received = pairs.pop("hash", None)
    if not received:
        raise ValueError("no hash")
    pairs.pop("signature", None)   # the OTHER protocol's input — see below

    check = "\n".join(f"{k}={pairs[k]}" for k in sorted(pairs))
    secret = hmac.new(b"WebAppData", bot_token.encode(), hashlib.sha256).digest()
    expected = hmac.new(secret, check.encode(), hashlib.sha256).hexdigest()

    if not hmac.compare_digest(expected, received):
        raise ValueError("bad signature")
    if time.time() - int(pairs["auth_date"]) > MAX_AGE:
        raise ValueError("stale")
    return pairs

次の 5 つは、いずれも誰かの環境では「有効」と判定されてしまう失敗例です。

  • インターネット上のコード例の半数では、キー導出の順序が逆です。 シークレットは HMAC_SHA256(key="WebAppData", msg=bot_token) です。定数が キー で、トークンが メッセージ です。順序を逆にすると、常に同じハッシュが生成されますが、一致することはありません。よくある解決策は、検証をやめることです。
  • キーでソートし、\n で結合して、受け取った値をそのまま使います — URL デコードは 1 回だけ行い、再エンコードも再シリアライズもしません。user は JSON 文字列なので、届いた文字列をそのまま保持する必要があります。解析して再ダンプするとキーの順序が変わり、ハッシュが一致しなくなります。
  • 文字列を組み立てる前に hash と signature を取り除きます — ただし、理由を正しく理解してください。 signature は不要なデータではありません。Ed25519 の経路への入力であり、別個の正規化処理({bot_id}:WebAppData のプレフィックスを付け、その後に hash と signature を除いたペアをソートする)を持つ別個のプロトコルです。2 つの正規化処理は fixtures/verify_initdata.py の hmac_check_string と ed25519_check_string のように、別々の関数にしてください。両方で共用するヘルパーは、正規化のバグをそのまま検証してしまうからです。フィクスチャのゴールデンベクトル(手作業で計算し、オラクルから導出したものではありません)がそのバグを検出します。signature を含む initData が通るのは、正しい経路だけです。
  • 重複や不正なデータは、計算の前に拒否します。 parse_qsl はリストを返します。これを辞書にまとめると、重複したキーの最後の値が黙って残ります。user=innocent&user=admin は HMAC がどちらの値を対象にしたのかという問題を引き起こします。検証器がそのような問題を抱えるべきではありません。重複フィールドや解析できないデータは、そのまま拒否してください。また、auth_date には上限時間も設けてください(クロックスキュー、秒単位)。未来のタイムスタンプは新しさを増すばかりです。
  • auth_date は省略できません。 有効期間を設けないと、取得された initData は永久に使えるベアラートークンになります。期間を決めて明示し、それより古いものは未認証として扱ってください。ログにエラーを記録して処理を続けるのではありません。
  • 定数時間で比較します。 16 進数のダイジェストを == で比較するとタイミングオラクルになります。言語に用意されている compare_digest 相当の関数を使うのに、コストはかかりません。

リクエストごとに検証しない

initData はログインであって、セッションではありません。一度検証したら、独自の短期間有効なトークンを発行し、それ以降は API でそのトークンを使います。

POST /auth/telegram   { initData }   →  verify → your session JWT (short TTL)
GET  /api/...         Authorization: Bearer <your token>

呼び出しのたびに再検証すると、長いセッションをカバーできるよう MAX_AGE を広く設定せざるを得ません。それでは auth_date をまったく確認しないのと同じです。

Bot トークンがない場合のサードパーティ向け経路

外部サービスは Bot トークンを保持できないため、Telegram は別の方法として Ed25519 の signature で initData に署名します。Telegram の公開鍵を使って検証できます。署名対象の文字列は異なり、<bot_id>:WebAppData と改行を先頭に付け、その後に hash と signature の両方を除いた同じソート済みの key=value ペアを続けます。公開鍵はテスト環境と本番環境で異なります。

検証器が Bot の所有者でない場合に使います。トークンを保持している場合に HMAC の代わりとして使わないでください。HMAC の経路のほうが単純で、変更されるキーも少なくて済みます。コードを含む両方の方式については、こちらを参照してください。 references/initdata-verification.md。

SDK のパッケージ名が変わった一方で、組織のページにはまだ古い名前が記載されています

  • 現行のパッケージ:@telegram-apps/sdk(2026-08-25 時点で 3.11.8)。@telegram-apps/sdk-react も併せて提供されています。
  • GitHub 組織の Telegram-Mini-Apps では、今も tma.js と @tma.js/sdk が案内されています。こちらは旧名称で、最終バージョンは 3.3.0 と、マイナーリリース 8 つ分遅れています。
  • この組織のテンプレート(reactjs-template、nextjs-template、vuejs-template、solidjs-template)は参考になります。ほかの 4 つはアーカイブ済みで、サポート対象外と明記されています。エージェントが検索結果の最初のものをコピーすると、約半分の確率でそのうちの 1 つに当たります。

どちらのライブラリも必須ではありません。Mini App インターフェースは telegram-web-app.js によって提供される window.Telegram.WebApp です。SDK で得られるのは型付きアクセスとプラットフォームのシムであり、機能そのものではありません。

デバイスはあなたのノートパソコンではありません

Mini App は、キーボード、ヘッダー、ホームインジケーターが表示領域を占める WebView 内でレンダリングされます。推測に頼らず、次のビューポートフィールドを参照してください。 viewportHeight、viewportStableHeight、safeAreaInset、 contentSafeAreaInset、isFullscreen、isActive。100vh はどちらのプラットフォームでも正しくなく、その誤り方も異なります。

テーマは themeParams と colorScheme から取得され、あなたの都合ではなくユーザーに応じて変わります。パレットをハードコードせず、テーマに連動させてください。 references/viewport-and-platform.md。

ホストはページの上位にあり、デバイスも同様です

以下の論文とドキュメントは、2026-10-19 時点のものです。

脅威モデル:Telegram クライアントは信頼されています。ページ内の何ものも、クライアントを拒否できないためです。 Priyanka ほかによる Don't Trust the Super-App: A Case Study of Russia's Max(arXiv:2609.11814、2026-09-10)では、MAX スーパーアプリを対象に、ホストが Mini App の UI をキャプチャし、ローカルストレージの読み書きを行い、実行環境に任意の JavaScript を注入し、ネットワークトラフィックを仲介し、認証コンテキストを制御できることを示しています。これらの機能により、気づかれないままなりすましが可能になります。著者らは、こうした機能は、あらゆるスーパーアプリが設計上持つ特権に由来すると論じています。この研究の対象は Telegram ではなく MAX です。境界のあり方を示すものとして捉えてください。署名済みの initData が証明するのは、クライアントが保証した内容であって、クライアントが誠実であることではありません。その前提を脅威モデルに明記し、秘密情報や判断はサーバー側に置き、ページが保持する情報は短期間だけ有効なものにしてください。

デバイス上の秘密情報は SecureStorage に保存し、読み取り可能なストレージには決して保存しないでください。 Ciccotelli、Zappone、Di Pietro による TENET: Telegram Mini App (in)security(arXiv:2608.17538)では、37 個の Mini App を分析し、30 個に 3 段階の欠陥(平文ストレージ、復号可能な暗号化、リプレイ可能なトークン)があり、保存されていた情報にはセッショントークンやウォレットのニーモニックが含まれていたことを明らかにしました。Bot API 9.0 以降、Telegram.WebApp.SecureStorage は iOS Keychain と Android Keystore を使用し、保存時に暗号化されます。1 ユーザー・1 ボットにつき最大 10 項目を保存できます。DeviceStorage は localStorage に相当するもので(1 ユーザー・1 ボットにつき 5 MB)、秘密情報の保存には使えません。短期間だけ有効なセッショントークンは前者に保存し、ニーモニックはいずれにも保存しないでください。

TON Connect 5.0 はバージョン番号の付け替えですが、3.0 からの変更はそれだけではありません。 2026-10-11、@tonconnect/ui と @tonconnect/ui-react は、@tonconnect/sdk とメジャーバージョンの系列をそろえるため、5.0.0 に移行しました。コードは ui 3.1.0 と同じです(5.0.1 は見た目に関するパッチです)。3.1.0 では、エラーハンドラーに渡される内容が変わりました。UI のエラークラスは error.name にクラス名を報告するようになりました(以前は "Error" でした)。また、既知のウォレット拒否は TonConnectUIError: Unhandled error ではなく、UserRejectsError のような型付き SDK エラーとしてアプリに届きます。Mini App の検出(isInTMA、getTmaPlatform など)は @tonconnect/sdk に移され、モバイルのモーダルには Telegram と Gram Wallet がそれぞれ独立したボタンとして表示されます。3.0.x からアップグレードする前に、型付きエラーに応じた処理を追加してください。

ボットに結果を返す方法

  • sendData(data) — キーボードボタンから開かれたアプリ向けです。アプリを閉じ、web_app_data をボットに届けます。ボットは、これがユーザーからの入力であることに変わりはないため、引き続きユーザー入力として扱う必要があります。
  • answerWebAppQuery — インラインボタンから開かれたアプリ向けです。検証済みの initData に含まれる query_id を使用します。有効期間は短く、先に時間のかかる処理を行うサーバーでは、期限切れになっているでしょう。
  • 支払いは、telegram-bots で説明されているのと同じ pre_checkout_query → successful_payment の手順で、Stars を使ってボット経由で処理します。 Mini App はユーザーがクリックする場所であり、支払いが確定する場所ではありません。

リリース前の確認事項

  1. サーバーは検証済みの initData から ID を特定します。クライアントから送信された ID は使用しません(§ initDataUnsafe という名前には理由があります)。
  2. auth_date は明示された期間と照合され、initData は独自のセッションと交換されます(§ 正確な検証方法、§ すべてのリクエストで検証しない)。
  3. ハッシュの比較は定時間で行われ、チェック文字列の作成前に hash と signature の両方が削除されます。
  4. SDK は @telegram-apps/* です。また、組織からコピーしたテンプレートがアーカイブ済みのものでないことを確認します(§ SDK のパッケージ名が変更されました)。
  5. レイアウトでは 100vh ではなく、安全領域とビューポートのフィールドを読み取ります(§ デバイスはあなたのノートパソコンではありません)。
  6. 脅威モデルではホストを信頼できるものとして扱い、ページが保持するシークレットは localStorage や DeviceStorage ではなく SecureStorage に保存します(§ ホストはページより上位にあります)。

トリガー

このページに誘導するその他のフレーズ:「miniapp」、「WebAppData」、「@telegram-apps/sdk」。

0.5.4 で説明からこちらに移動しました:「telegram web app」、「телеграм веб-апп」、「валидация initData」。

コメント