Mintlify脆弱性はReact2Shellとは別物:フレームワークの責任と実装者の責任
Mintlifyで複数の脆弱性が発見された
2025年12月、ドキュメンテーションプラットフォーム「Mintlify」で複数の深刻な脆弱性が発見された。Discord、Vercel、Cursorなど多くの企業が利用しているサービスだ。
発見された脆弱性の中で最も深刻だったのは、MDX(Markdown + JSX)のサーバーサイドレンダリング時に任意のJavaScriptが実行可能だったというもの(CVE-2025-67843)。
// こんなMDXを書くだけでサーバー上でコードが実行される
{process.env.SECRET_KEY}
{require('fs').readFileSync('/etc/passwd', 'utf8')}
環境変数やファイルシステムへのフルアクセスが可能で、さらにNext.jsのキャッシュを汚染して他社のMintlify利用サイトでXSSや改ざんまでできたという。
詳細は発見者のレポートを参照:how to hack discord, vercel and more with one easy trick
React2Shellとは別の話
最近Next.js/React周りで騒がれていた「React2Shell」(CVE-2025-55182、CVSS 10.0)があったので、最初は関連する話かと思った。しかし調べてみると全く別物だった。
| React2Shell | Mintlify | |
|---|---|---|
| 原因 | RSCプロトコルのデシリアライゼーション脆弱性 | ユーザー入力のサーバーサイドeval |
| 責任 | Reactコア(フレームワーク側) | Mintlifyの実装(実装者側) |
| 攻撃条件 | HTTPリクエストを送るだけ | Mintlifyの顧客である必要 |
| 影響範囲 | React 19 + RSC実装全般 | Mintlifyのみ |
React2Shellはフレームワーク側の問題。RSC(React Server Components)のFlightプロトコルにおけるデシリアライゼーション処理に脆弱性があり、特殊なHTTPリクエストを送るだけでRCEが可能だった。これはReact/Next.jsを使っている時点で影響を受ける可能性がある。
一方、Mintlifyの脆弱性は実装者側の問題。「ユーザーが書いたMDXをサーバーサイドでevalしていた」という古典的なミスだ。
フレームワーク非依存の問題
Mintlifyの脆弱性はNext.js固有の問題ではない。同じ設計をすれば、NuxtでもSvelteKitでも同じことが起きる。
// これをサーバーサイドでやったらダメ、というだけの話
eval(userProvidedCode)
new Function(userProvidedCode)()
vm.runInContext(userProvidedCode)
「信頼できないコードをサーバーで実行するな」というセキュリティの基本中の基本。MintlifyがたまたまNext.jsで動いていただけで、本質はフレームワークとは関係ない。
Astroはどうなのか
では静的サイトジェネレーターのAstroはどうか。
Astroの場合、MDXはビルド時に処理される。本番環境では静的なHTMLが配信されるだけなので、Mintlifyのような「サーバーサイドでユーザー入力を動的に評価する」という攻撃面がそもそも存在しない。
ただし、AstroでもSSRモードを使い、さらにユーザー入力をサーバーで動的にevalするような実装をすれば、同じ穴は掘れる。フレームワークが守ってくれるわけではない。
結局のところ:
- SSGデフォルト → 攻撃面が狭い(Astro、Hugo、11tyなど)
- SSRデフォルト → 攻撃面が広い(Next.js、Nuxt、SvelteKitなど)
ただしSSRが悪いわけではなく、SSRでも適切に実装すれば問題ない。問題なのは「ユーザー入力をサーバーでeval」という実装判断だ。
正直な感想
発見者には$5,000の報奨金が支払われた。発見と報告は素晴らしい仕事だと思う。
ただ、正直なところ「こんな基本的なミスが通ってたの?」という気持ちがある。
「ユーザー入力をサーバーでevalするな」なんて、セキュリティの初歩中の初歩だ。おそらくこういう流れだったんだろう:
- 「MDXでJS式使えたら便利じゃん」
- 開発環境で動いた、便利
- 本番でもそのまま動かす
- 「これサーバーで動いてるんだけど」を誰も気づかない
MDXの「Markdownの中にJSX書ける」という便利さが、「それサーバーでevalしていいの?」という視点を曇らせたのかもしれない。DX(開発体験)を優先しすぎてセキュリティが後回しになる典型例だ。
コードレビューで誰も気づかなかったのか、そもそもレビューがなかったのか。Fortune 500企業が使うサービスでこれが通っていたというのは、考えさせられるものがある。
結論:責任の所在を区別する
セキュリティインシデントが起きたとき、それが「フレームワーク側の責任」なのか「実装者側の責任」なのかを区別することは重要だ。
フレームワーク側の責任(例:React2Shell)
- フレームワークを使っている時点で影響を受ける
- 対応:パッチ適用、バージョンアップ、場合によってはフレームワーク移行
実装者側の責任(例:Mintlify)
- 特定の実装パターンを採用した場合のみ影響
- 対応:実装を見直す、セキュリティレビューを行う
Mintlifyの件を見て「Next.jsは危険だ」と判断するのは早計。同時に、React2Shellを見て「うちはNext.js使ってないから関係ない」と判断するのも危険かもしれない(Nuxt等でもRSC的な機能が将来実装される可能性はある)。
重要なのは、何が原因で、誰の責任範囲の問題なのかを正しく理解すること。そうすれば適切な対応ができる。
とはいえ、自分はAstroに移行した
理屈では「責任の所在を区別しろ」と言ったが、実際の運用ではそう単純ではない。
React2Shellが発覚したとき、すぐにパッチを当てた。数日後「初回修正が不完全でした」と追加の脆弱性。また当てた。さらに数日後「まだ脆弱でした」。
正直、めんどくさくなった。
結局、管理していたコーポレートサイトはNext.jsからAstroに移行した。SSGで静的HTMLを吐くだけなら、そもそもサーバーサイドの攻撃面がない。アップデート地獄からも解放される。
Mintlifyの件は実装者の責任だし、React2Shellはフレームワークの責任だ。区別は大事。でも「どちらの責任か」を考える時間すら惜しいなら、攻撃面が狭い選択肢を取るのも一つの判断だと思う。
Astroはフロントエンドをやってきた人なら移行は楽だと思う。移せるタイミングがあるなら移しちゃってもいいんじゃないだろうか。大規模なサイトや動的な要件が多いときにNext.js(またはNuxt)を検討すればいい。
まあ、私はNuxtは選択肢に入れるけど、Next.jsは最後まで入れないと思う。
Next.jsとNuxtには設計思想の違いがあって、個人的にNext.jsの方が好みじゃないというのもある。これについては長くなるので、機会があればそのうち。