はじめに
Webアプリのテンプレートエンジンは、サーバー側で HTML を動的生成するための便利な仕組みです。しかし、そのテンプレート機能が外部入力と誤って結合されると――忍び寄るのが SSTI(Server-Side Template Injection) です。
一見ただのテンプレート構文に見えても、実は「任意コード実行」ができてしまうケースがあり、攻撃者からすると“宝箱”。開発者からすると“恐怖の落とし穴”です。
この記事では、SSTI の 基礎から実例、攻撃の仕組み、防御方法 まで、まとめて理解できるように解説します。
SSTI とは?
SSTI(Server-Side Template Injection)=サーバー側テンプレートへのインジェクション攻撃
テンプレートエンジン(Jinja2、Twig、Freemarker、Velocity など)がユーザー入力を直接レンダリングし、テンプレート構文として解釈されてしまうと発生します。
短く言えば:
「テンプレートエンジンが“実行しちゃダメな文字列”まで実行してしまう脆弱性」
起きる条件
- サーバー側テンプレートエンジンを使っている
- ユーザー入力がテンプレートとして評価される
- 開発者が
{value}のように「気軽に埋め込む書き方」をしてしまう
なぜ危険なの? — “RCEの一歩手前”
テンプレートエンジンは、ただの文字置換ツールではありません。
多くのエンジンには 変数・フィルタ・関数・オブジェクト操作 があり、中には Python の任意クラス参照、システムコマンド実行 にアクセスできるものが存在します。
結果として、SSTI は以下を可能にします:
- 変数値の改ざん
- サーバー内部情報の取得(PATH、環境変数など)
- ファイル読み取り
- 任意コード実行(RCE)
- サーバー乗っ取り
つまり…
SSTI は「XSS のサーバー版 + RCEのおまけ付き」
と言っても過言ではありません。
典型的な発生例(Jinja2)
NGコード例
return render_template_string("Hello " + user_input)
攻撃者が次のような値を送ると?
{{ 7*7 }}
→ テンプレートエンジンが評価
→ 結果は 49
さらに…
{{ ''.__class__.__mro__[2].__subclasses__()[40]('ls',shell=True,stdout=-1).communicate()[0] }}
→ Python のサブクラスをたどって「subprocess.Popen」へアクセス
→ ls コマンド実行
こうして RCE が成立します。
他テンプレートエンジンの例
Twig(PHP)
{{ 7*7 }} → 49
{{ constant('PHP_OS') }} → OS 情報
Freemarker(Java)
${"freemarker.template.utility.Execute"?new()("id")}
→ OS コマンド実行
Velocity(Java)
#set($e = ''.getClass().forName('java.lang.Runtime').getRuntime())
$e.exec('id')
どの言語にも存在しているのです。
攻撃者が SSTI を発見する流れ
攻撃者は小さな“注入テスト”から始めます。
Step1:テンプレート構文が効くかを確認
{{7*7}}${7*7}<%= 7 * 7 %>
Step2:エラーメッセージ・レスポンスの変化を見る
- 計算結果が出る?
- 例外が変わる?
- テンプレートエンジン名が漏れる?
Step3:オブジェクト参照ルートを探索
- Python → MRO ルート
- Java → メタクラスや Runtime
- PHP → 定数参照や関数呼び出し
少しずつ“階段を上るように”RCE に近づいてきます。
防御策:実はシンプル
SSTI の防御は「テンプレートにユーザー入力を直接埋めない」これに尽きます。
1. 文字列としてエスケープして扱う
{{ user_input | e }} # Jinja2 の HTML エスケープ
2. render_template_string を使わない
テンプレートは必ずファイル側で定義。
3. ブラックリストではなく「ホワイトリスト」方式で許可
例:
if username not in allowed_list: reject
4. テンプレートエンジンの sandbox 機能を有効化
- Jinja2 → SandboxedEnvironment
- Twig → sandbox extension
5. WAF でテンプレート構文をフィルタする
ただし完全防御は難しいので最後の補強として。
まとめ
SSTI は、シンプルな見た目とは裏腹に 最悪の場合サーバー乗っ取りまで発展する強力な脆弱性 です。
テンプレートエンジンが“便利すぎる”ことが原因であり、開発者はその強力さを理解したうえで安全に扱う必要があります。
- 入力を直接テンプレートに入れない
- 必ずエスケープ
- テンプレートはコードと分離
- Sandbox を有効化
このあたりを徹底すれば、SSTI に怯える必要はありません。