[緊急警報] TinaCMSに深刻なアクセス制御の脆弱性 (CVE-2026-63506 / GHSA-g74q-6g2f-874x)
はじめに
TinaCMSは、GitベースのヘッドレスCMSとして、コンテンツ管理の柔軟性から多くのフロントエンド開発者に利用されています。特にセルフホスト環境での運用は、高いカスタマイズ性とデータの管理権限が魅力です。 しかし、先日TinaCMSの認証ライブラリである`@tinacms/auth`に深刻な脆弱性(CVE-2026-63506 / GHSA-g74q-6g2f-874x)が報告されました。この脆弱性は、TinaCloud認証を使用しているセルフホストサイトに重大な影響を及ぼす可能性があります。本記事では、この脆弱性の技術的な詳細、影響範囲、そして日本のフロントエンドエンジニアが取るべき対策について解説します。
脆弱性の概要 (CVE-2026-63506)
この脆弱性は「Broken Access Control(アクセス制御の不備)」に分類され、深刻度は「High」と評価されています。 **影響を受けるコンポーネント:** * `@tinacms/auth` パッケージ * `TinaCloudBackendAuthProvider()` を使用しているバックエンド * `next-tinacms-cloudinary`、`next-tinacms-s3`、`next-tinacms-dos` などのメディアハンドラ
**根本原因:** TinaCMSの認証処理において、ユーザー認証に使用される`clientID`(TinaCloudアプリケーションID)が、本来サイトが設定すべき固定値ではなく、リクエストURLのクエリパラメータから受け取った値を使用していました。これにより、攻撃者は自身のTinaCloudアプリの`clientID`と有効なトークンを使い、本来アクセス権限のない被害者サイトへの認証を通過できてしまうのです。
なぜこんなことが起こるのか? 技術的な詳細
問題は、`@tinacms/auth`パッケージ内の`isAuthorized`関数にあります。この関数は以下のようなロジックで動作していました。 ```ts export const isAuthorized = async (req: NextApiRequest) => { const clientID = req.query.clientID; // <-- 攻撃者によって制御可能 const token = req.headers.authorization; // <-- 攻撃者によって制御可能 if (typeof clientID === 'string' && typeof token === 'string') { return await isUserAuthorized({ clientID, token }); } return undefined; }; ``` `clientID`が`req.query.clientID`から直接取得され、`isUserAuthorized`関数に渡されます。`isUserAuthorized`関数は、この`clientID`を使って`https://identity.tinajs.io/v2/apps/${clientID}/currentUser`エンドポイントに問い合わせを行い、提供された`token`がその`clientID`に紐づく有効なユーザーのものであるかを確認します。 **ここが問題です。** 攻撃者は自身でTinaCloudの無料アカウントを作成し、自身のアプリケーションID(`ATTACKER_APP`)と有効なトークン(`T_attacker`)を取得できます。そして、被害者サイトに対して、自身の`clientID`とトークンを付与してリクエストを送ります。すると、認証サーバーは「`ATTACKER_APP`の認証済みユーザーか?」という問いに対して`true`を返してしまいます。被害者サイトは、この結果を基に攻撃者を「認証済みユーザー」と誤って判断し、アクセスを許可してしまうのです。被害者サイト自身の`clientID`との比較が行われないため、このようなクロステナント(異なるユーザー間での)認証バイパスが発生します。
具体的な影響範囲
この脆弱性が悪用されると、攻撃者はTinaCloudの無料アカウントを保有しているだけで、無関係なセルフホストのTinaCMSサイトに対して、エディターレベルの制御権限を得る可能性があります。具体的な影響は以下の通りです。 * **メディアハンドラ (`next-tinacms-cloudinary`, `next-tinacms-s3`, `next-tinacms-dos`など):** * メディアバケット内のファイルの一覧表示、読み取り、アップロード、削除。 * 攻撃者は、被害者サイトのCDNを利用してマルウェアやフィッシングサイトのホスティングに悪用する可能性があります。 * **`TinaCloudBackendAuthProvider()` を使用しているバックエンド:** * 任意のGraphQL操作が可能になります。 * すべてのドキュメントの読み取り、`createDocument` / `updateDocument` によるコンテンツの改ざんや不正なコンテンツの注入、`deleteDocument` によるコンテンツの破壊など、ウェブサイトの完全な制御が可能になります。
攻撃の再現例
攻撃は、自身のTinaCloudアプリIDとトークンを使って、被害者サイトの既知のTinaCMSエンドポイントに対して行われます。例えば、以下のリクエストが本来401/403となるべきところ、200で成功してしまいます。 **メディアの読み取り (GETリクエスト):** ``` GET /api/cloudinary/media?clientID=ATTACKER_APP HTTP/1.1 Host: victim.example Authorization: T_attacker ``` **コンテンツの削除 (POSTリクエスト - GraphQL):** ``` POST /api/tina/gql?clientID=ATTACKER_APP HTTP/1.1 Host: victim.example Authorization: T_attacker Content-Type: application/json {"query":"mutation($c:String!,$r:String!){deleteDocument(collection:$c,relativePath:$r){__typename}}","variables":{"c":"post","r":"hello.md"}} ``` これらの攻撃により、攻撃者は被害者サイトにアカウントを持っていなくても、メディアの操作やコンテンツの改ざん、削除といった編集者レベルの操作が可能になります。
フロントエンドエンジニアとして取るべき対策
この脆弱性に対処するためには、以下の対策を速やかに実施することが不可欠です。 1. **TinaCMS関連ライブラリのアップデート:** 影響を受ける`@tinacms/auth`および関連するTinaCMSライブラリを、脆弱性が修正された最新バージョンに速やかにアップデートしてください。通常、セキュリティアップデートはパッチバージョンアップとして提供されます。 2. **コードの確認と修正:** アップデート後も、認証ロジックがサイト自身の`clientID`を正しく参照しているか確認することが重要です。修正案では、`isAuthorized`関数に`expectedClientID`という引数を追加し、リクエストで渡された`clientID`がサイト自身のものと一致するかを厳密にチェックするよう変更されています。 **修正コードの例 (diff形式):** ```diff - export const isAuthorized = async (req: NextApiRequest) => { - const clientID = req.query.clientID; - const token = req.headers.authorization; + export const isAuthorized = async (req: NextApiRequest, expectedClientID?: string) => { + const requestClientID = req.query.clientID; + const token = req.headers.authorization; + const clientID = expectedClientID ?? process.env.NEXT_PUBLIC_TINA_CLIENT_ID; + if (expectedClientID && requestClientID && requestClientID !== expectedClientID) { + return undefined; // refuse a cross-tenant clientID + } if (typeof clientID === 'string' && typeof token === 'string') { return await isUserAuthorized({ clientID, token }); } return undefined; }; ``` この修正により、`isAuthorized`関数は、サイトが期待する`clientID`(環境変数`NEXT_PUBLIC_TINA_CLIENT_ID`から取得されるか、引数で明示的に渡される)と、リクエストで提供された`clientID`を比較し、一致しない場合は認証を拒否するようになります。 また、メディアハンドラや`TinaCloudBackendAuthProvider`を呼び出す箇所で、サイト自身の`clientID`を明示的に渡すように変更する必要があります。
まとめ
TinaCMSの認証におけるアクセス制御の不備は、セルフホスト環境のサイトにとって非常に危険な脆弱性です。攻撃者がTinaCloudの無料アカウントを持つだけで、貴社のウェブサイトのコンテンツを自由に操作できてしまう可能性があります。 フロントエンドエンジニアの皆様は、ご自身のTinaCMSプロジェクトでTinaCloud認証を利用している場合、速やかにライブラリを最新版にアップデートし、認証ロジックがサイト自身の`clientID`を適切に検証しているかを確認してください。外部からの入力に依存する認証パラメータは、常に厳密な検証が必要であるというセキュリティの基本原則を再認識する良い機会となるでしょう。