Skip to main content

慌てないで:Thymeleafのテンプレートインジェクションは、許さなければ被害を受けない(CVE-2026-40478)

2026年4月29日

0 分で読めます

CVSSスコア9.1のThymeleafの脆弱性は、注目を集めるだけの重大さがあります。しかし、騒ぎ立てて新たなLog4Shellだと決めつける前に、まずこの記事を読んでください。

CVE-2026-40478は、ペネトレーションテスターのDawid Bakajが発見した、Thymeleafのサーバーサイドテンプレートインジェクション脆弱性です。Thymeleafは、サーバーサイドでのWebページのレンダリングに使われるJavaのテンプレートエンジンです。任意のコード実行を通常は防ぐサンドボックスが、タブ文字を使って回避されました。そして、悪用されるとリモートコード実行につながる可能性があります。

ただし、最も重要なのはここです。この脆弱性が影響するのは、コードがすでに本来すべきでない処理をしている場合に限られます。

サンドボックスが防ぐもの

Thymeleafにはセキュリティサンドボックスがあります。動的コンテンツを評価する際に、SpEL(Spring Expression Language)式が実行できる処理を制限します。このサンドボックスが必要となるのは、ユーザーが制御する入力が何らかの形でThymeleafの式エンジンに渡る場合です。

コード内でそのようなことが起きなければ、サンドボックスは使われず、このCVEの影響も受けません。Thymeleafの正しい使い方はシンプルです。ユーザー入力はデータモデルに渡し、テンプレートはほとんどの場合、HTMLファイル内で静的に保ちます

Javaコード:

@GetMapping("/greet/safe")
public String greetSafe(@RequestParam String name, Model model) {
   model.addAttribute("name", name);
   return "greet";
}

Thymeleafテンプレート:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<body>
   <p th:text="${name}">Default name</p>
</body>
</html>

Thymeleafはnameの値をレンダリングします。値を式として解析することはありません。攻撃者がname,にどんなペイロードを送っても、特に何も起こりません。これが設計どおりの動作であり、Thymeleafの本来の使い方です。

テンプレートエンジンの悪用

開発者がユーザー入力を式エンジンに直接渡すと、この脆弱性を悪用できるようになります。これはフレームワークの誤用を意味します。実現は可能ですが、特にThymeleafをSpring Bootで使う場合、かなり難しいことです。

@ResponseBody
@GetMapping("/greet/unsafe")
public String greetUnsafe(@RequestParam String name, Model model) {
   Context context = new Context();
   SpringTemplateEngine templateEngine = new SpringTemplateEngine();
   templateEngine.setTemplateResolver(new StringTemplateResolver());
   context.setVariable(name, name);
   String template = "<p th:text=\"'Hello ' + ${"+ name + "}\">...</p>";
   return templateEngine.process(template, context);
}

上記のコードにあるように、templateEngineのインスタンスを手動で作成し、リゾルバーを設定する必要があります。通常ならSpringが提供する処理です。
さらに、通常の正当なユースケースを動作させるため、コンテキストに変数を設定する必要もあります。

しかし上記のケースでは、ユーザー入力が式エンジンに渡されています。これが、このCVEが問題となる前提条件です。

動的なビュー解決も、同様の誤用パターンです。テンプレート文字列を直接組み立てる代わりに、開発者がユーザー入力に基づいてビューを解決することがあります。

@GetMapping("/page/unsafe")
@ResponseBody
public String showPageUnsafe(@RequestParam String page) {
   Context context = new Context();
   SpringTemplateEngine templateEngine = new SpringTemplateEngine();
   StringTemplateResolver resolver = new StringTemplateResolver();
   resolver.setTemplateMode(TemplateMode.TEXT);
   templateEngine.setTemplateResolver(resolver);
   context.setVariable(page, page);
   String template = "[[${" + page + "}]]";
   return templateEngine.process(template, context);
}

1つのエンドポイントで複数のページを提供する正当なユースケースです。しかし、ユーザー入力がThymeleafの解析対象に影響するようになっています。同じ誤用パターンであり、同じように悪用される可能性があります。ここに至るまでに、手動でどれほど多くの設定が必要だったか、改めて注目してください。安全な方法なら、今でもわずか3行です。

@GetMapping("/page/safe")
public String showPageSafe(@RequestParam String page) {
   return page;
}

タブ文字がThymeleafのサンドボックスを回避する仕組み

誤用パターンが存在すれば、実際の悪用は簡単です。

サンドボックスは、オブジェクトのインスタンス化を防ぐために、new(キーワードの後にスペースが続く形式)をチェックします。このチェックを回避するため、代わりにnew[TAB]を送ります。事前チェックではnewが見つからず、そのまま通過します。一方、SpELはタブを有効な空白文字として扱い、正しく解析します。

ペイロードは次のようになります:

new[TAB]org.springframework.core.io.FileSystemResource('shell.jsp').getOutputStream()

キーワードのチェック後には、java.*クラスをブロックする型のブロックリストが適用されます。しかし、Springのクラスはこのリストに含まれていませんでした。その結果、不完全なブロックリストとnewに対する不十分なフィルターにより、FileSystemResourceのようなクラスの読み込みが可能になりました。これにより、ディスクにファイルが書き込まれました。理論上、攻撃者はProcessBuilderを呼び出してRCEを実行するJSPファイルを配置できます。

2つの防御がそれぞれ独立して失敗しました。キーワードチェックにおける空白文字の問題と、対象が限定的なブロックリストです。区切り文字と見なされるもののチェックが不完全だったため、回避が成功しました。詳しく知りたい方は、Endor Labsによる詳細な分析をご覧ください。

安全なコードと危険なコードの動作例は、GitHubリポジトリをご覧ください

必要な対応

まずパッチを適用してください。コードが影響を受けると思うかどうかにかかわらず、Thymeleaf 3.1.4にアップデートしてください。監査結果を待ってはいけません。

Spring Boot経由でThymeleafを使用している場合は、Spring Bootのstarter parentを最新バージョン(現在は3.5.14または4.0.6)にアップデートするだけです。数バージョン遅れている場合は、OpenRewriteのレシピを使って最新バージョンのSpring Boot 3.5またはSpring Boot 4.0に移行することを検討してください

アップデート後、テンプレート文字列やページ解決が、ユーザー入力をテンプレートエンジンに直接渡すことで動的に生成されていないか、コードを確認してください。そのような処理があれば、誤用パターンです。ライブラリのバージョンだけでなく、コード自体を修正してください。

CVSSスコア9.1は妥当。ただし条件付き

重大なCVSSスコアは、前提条件が満たされることを想定しています。コードがユーザー入力をThymeleafの式エンジンに渡しているなら、スコア9.1は妥当であり、影響も深刻です。そうでなければ、このCVE自体の影響はありません。ただし、パッチは適用してください!

チームに確認すべき監査上の問いは、「Thymeleafのバージョンはいくつか」だけではありません。「リクエストデータからビュー名やテンプレート式を動的に構築しているか」も確認してください。

Thymeleafをバージョン3.1.4以降にアップデートし、次にその問いへの答えを確認してください。答えにかかわらず、開発中は依存関係を継続的にスキャンし、本番環境ではSnyk Open Source.で監視しましょう。しかも、無料ですぐに始められます。

Pythonアプリのセキュリティ対策を始めましょう

Snykを使って、Pythonの脆弱性を無料で検出・修正できます。

クレジットカードの登録は不要です。

またはAzure AD、Docker ID、Bitbucketで登録

Snykを利用すると、利用規約およびプライバシーポリシーを含む各種ポリシーに同意したものとみなされます。

続きを読む

Blog

フロンティアモデルは脆弱性を発見した。攻撃者だけがエクスプロイトチェーンを見つけた。

静的解析で欠陥は見つかりましたが、ライブ攻撃テストで侵害につながる連鎖を実証できたのは唯一でした。Evo COS、Claude Security、Claude Code Securityを比較します。

feature insights context
Blog

自律型攻撃はすでに始まっている。防御もそのスピードに追いつかなければならない。

自律型攻撃者によって、防御に使える時間は短くなっています。継続的な検出、修復、検証、予防で、セキュリティチームが攻撃に歩調を合わせる方法をご紹介します。

Blog

AIコーディングエージェントが不適切なアクセス制御を繰り返し実装する理由

AIコーディングエージェントは、コンパイルが通りレビューも通過する一方で、あるテナントのデータを別のテナントに公開してしまう認可ロジックを生成することがあります。不適切なアクセス制御が検出しにくい理由と、その防止策をご紹介します。