2026年2月5日、Next.jsベースのヘッドレスCMS「Payload」に、CVSS 9.8のクリティカルなSQLインジェクション脆弱性(CVE-2026-25544)が公表された。Drizzle ORMアダプタを使用するPostgreSQL/SQLite環境において、JSON・richTextフィールド経由で**認証不要**のブラインドSQLインジェクションが可能となり、データベース全体の窃取やアカウント乗っ取りに直結する構造的欠陥が露呈した。本記事では、攻撃メカニズムの詳細とCMS選定時のセキュリティ評価基準を解説する。

CVE-2026-25544の概要と影響範囲

CVE-2026-25544は、Payload CMSのnpmパッケージ@payloadcms/drizzleに存在したSQLインジェクション脆弱性である。v3.73.0未満の全バージョンが影響を受け、Drizzle ORMを介してPostgreSQLまたはSQLiteに接続している環境が対象となる。MongoDB Atlasアダプタを使用している環境は影響を受けない。

脆弱性の条件

攻撃が成立するには、以下の条件を満たす必要がある:

  • Payload CMS v3.73.0未満を使用
  • Drizzle ORMアダプタでPostgreSQLまたはSQLiteに接続
  • type: 'json'またはtype: 'richText'フィールドを持つコレクションが存在
  • 該当フィールドのアクセス制御がaccess.readfalse以外(trueまたは条件付きアクセス)を返す

特に問題なのは、**認証を必要としない**点である。パブリックに読み取り可能なJSON/richTextフィールドが存在すれば、外部攻撃者が直ちに攻撃を実行できる。CVSS v3.1のスコアは9.8 (Critical)で、攻撃複雑度Low・特権不要・ユーザー操作不要という最悪の組み合わせを示している。

攻撃メカニズム:getBuildQueryJoinQuery関数の欠陥

脆弱性の根本原因は、Drizzle ORMアダプタ内のgetBuildQueryJoinQuery関数にある。この関数は、JSONやrichTextフィールドへのクエリを処理する際、ユーザー入力を**エスケープせずに直接SQL文に埋め込んでいた**。

脆弱なコードパターン

問題のコードは、jsonQuery変数をパラメータ化せずに文字列結合でSQL文を生成していた。例えば、以下のような構造である:

const sql = `SELECT * FROM table WHERE json_field @> '${jsonQuery}'`;

攻撃者は、JSONクエリパラメータに以下のようなペイロードを挿入することで、ブラインドSQLインジェクションを実行できる:

{"field": "value' AND (SELECT CASE WHEN (SELECT substring(password,1,1) FROM users LIMIT 1) = 'a' THEN pg_sleep(5) ELSE pg_sleep(0) END) --"}

このペイロードは、データベースのレスポンス時間を観測することで、1文字ずつパスワードハッシュやリセットトークンを抽出できる(タイムベースドブラインドSQLインジェクション)。

実際の攻撃シナリオ

  1. 偵察:攻撃者は、Payload CMSの管理パネルやAPIエンドポイントを探索し、JSON/richTextフィールドを持つコレクションを特定する
  2. データ抽出:ブラインドSQLインジェクションでusersテーブルからメールアドレス・パスワードハッシュ・リセットトークンを窃取する
  3. アカウント乗っ取り:窃取したリセットトークンを使用して管理者アカウントをハイジャックし、CMS全体の制御を奪取する
  4. 横展開:管理者権限で他のコレクションや環境変数にアクセスし、APIキーやデータベース認証情報を取得する

この攻撃は、従来のWebアプリケーションファイアウォール(WAF)では検出が困難である。ペイロードがJSON形式で送信されるため、通常のSQLインジェクション検出ルールをバイパスできるためだ。

Drizzle ORMとPayloadのアーキテクチャ

Payloadは、Next.js 15のApp Routerに直接組み込める「Next.js Native CMS」として設計されている。従来のヘッドレスCMSとは異なり、フロントエンドとバックエンドを分離せず、/appディレクトリ内に管理パネルとAPIを統合できる点が特徴である。

データベース層には、Drizzle ORMを採用している。Drizzle ORMは、TypeScript型安全性と高パフォーマンスを両立するORMで、PostgreSQL・SQLite・MySQL・Vercel Postgresなど複数のデータベースに対応する。Payloadは、データベースアダプタパターンを採用し、payload.dbプロパティ経由でDrizzleの全機能にアクセスできる設計となっている。

開発体験重視の「Push Mode」が裏目に

Payloadは、開発時にDrizzle ORMの「Push Mode」を使用する。これは、スキーマ定義をコードで記述すると、自動的にデータベースへDDLを発行してテーブルを同期する機能である。マイグレーションファイルを手動で書く必要がなく、開発速度が向上する一方、**セキュリティレビューの機会が減少する**というトレードオフが存在する。

今回の脆弱性は、自動生成されたクエリビルダー関数内に潜んでおり、手動でSQLを記述していれば発見できた可能性が高い。開発者体験(DX)とセキュリティのバランスは、モダンなCMS選定における重要な評価軸となる。

パッチと緊急回避策

Payloadチームは、2026年2月5日にv3.73.0をリリースし、脆弱性を修正した。修正内容は、getBuildQueryJoinQuery関数内でプリペアドステートメント(パラメータ化クエリ)を使用するよう変更したものである。

即座にアップグレードできない場合の回避策

パッチ適用までの一時的な対策として、以下の設定をすべてのJSON/richTextフィールドに適用することが推奨される:

{
  name: 'metadata',
  type: 'json',
  access: {
    read: () => false  // 一時的にすべてのアクセスを拒否
  }
}

ただし、これは機能停止を意味するため、実運用環境では以下の対応を優先すべきである:

  1. 緊急アップグレード:v3.73.0へのアップグレードを最優先で実施する
  2. ログ監査:過去のアクセスログを解析し、異常なJSON/richTextクエリパターンがないか確認する
  3. トークン無効化:パスワードリセットトークンやセッショントークンをすべて無効化し、ユーザーに再発行を促す
  4. インシデント対応:データ窃取の痕跡がある場合、GDPR等の規制に基づく通知義務を履行する

CMS選定時のセキュリティ評価基準

CVE-2026-25544は、ヘッドレスCMS選定における以下の評価基準の重要性を浮き彫りにした。

1. ORM・クエリビルダーの成熟度

Drizzle ORMは比較的新しいプロジェクトであり、セキュリティ監査の履歴が浅い。一方、Prisma ORM・Sequelize・TypeORMなど、長年の実績を持つORMは、SQLインジェクション対策が成熟している。CMS選定時には、採用するORMのセキュリティトラックレコードを確認すべきである。

2. アクセス制御の粒度

Payloadは、フィールド単位・ドキュメント単位・コレクション単位でアクセス制御を定義できるが、今回の脆弱性は「読み取り可能なフィールド」が攻撃の起点となった。**デフォルトdeny・必要最小限の権限付与**の原則に基づき、パブリックアクセスを許可するフィールドは慎重に選定する必要がある。

3. セキュリティ脆弱性の開示体制

Payloadは、GitHubセキュリティアドバイザリ(GHSA-xx6w-jxg9-2wh8)を通じて迅速に情報開示を行い、CISAの脆弱性サマリーにも掲載された。透明性の高い開示体制は、エンタープライズ採用における信頼性の指標となる。

4. WAF・ランタイム保護の組み合わせ

JSON形式のペイロードを用いたSQLインジェクションは、従来のシグネチャベースWAFでは検出が困難である。Cloudflare WAF・AWS WAF・Azure WAFを導入する場合、**カスタムルール**でJSON/richTextエンドポイントへの異常なクエリパターンを検出する必要がある。また、Datadog ASM・Snyk Runtimeなどのランタイムアプリケーション自己防御(RASP)ソリューションとの併用が有効である。

5. Supply Chain Security

Payloadは、npm経由で配布されるNode.jsパッケージである。package-lock.jsonの固定・npm auditの定期実行・Dependabotによる自動アップデートなど、サプライチェーンセキュリティの基本対策を徹底すべきである。特に、@payloadcms/drizzleのような「アダプタパッケージ」は、コア機能と独立してアップデートできるため、迅速なパッチ適用が可能である。

FAQ

MongoDB Atlasアダプタを使用していれば安全か?

はい。CVE-2026-25544は、Drizzle ORMアダプタ(PostgreSQL/SQLite)に特有の脆弱性であり、MongoDB Atlasアダプタは影響を受けない。ただし、MongoDB特有のNoSQLインジェクション対策は別途必要である。

v3.73.0へのアップグレードで互換性は保たれるか?

はい。Payloadの公式アナウンスによれば、v3.73.0はセキュリティパッチのみを含むマイナーリリースであり、API互換性は維持されている。ただし、本番環境への適用前にステージング環境でのテストを推奨する。

既にデータ窃取が発生しているか確認する方法は?

アクセスログを解析し、JSON/richTextフィールドへのクエリで異常に長いレスポンス時間(例:5秒以上の遅延)や、pg_sleepWAITFOR DELAYなどの文字列を含むリクエストがないか確認する。また、データベース監査ログで、想定外のSELECTクエリが実行されていないかチェックする。

Strapi・Contentful・Sanityなど他のCMSは安全か?

各CMSには異なるアーキテクチャとセキュリティモデルがある。Strapiは過去にRCE脆弱性(CVE-2023-22621)が報告されており、Contentfulはマネージドサービスのため脆弱性管理はベンダー側が担う。Sanityは、Content Lakeアーキテクチャによりクライアントから直接SQLにアクセスしない設計だが、GraphQL APIの権限管理には注意が必要である。

Next.js 15との統合におけるセキュリティ上の懸念は?

Payloadは、Next.js 15のApp Router・Server Components・Server Actionsと深く統合しているが、この統合自体が新たな攻撃面を生む可能性がある。特に、Server ActionsでCMS APIを呼び出す場合、CSRFトークン検証やRate Limitingを適切に実装する必要がある。Next.js 15.0系の初期バージョンにはReactのセキュリティ問題も報告されているため、最新の安定版(15.1系以降)を使用すべきである。

参考文献