【実務・中級編】テンプレートインジェクション(SSTI)のメカニズム – アプリケーションセキュリティ & 安全な開発防御ガイド

テンプレートインジェクション(SSTI)の深淵:RCEを許す「魔の数式」の正体

現場でインシデント対応をしていると、SQLインジェクションやXSSは「過去の遺物」のように語られがちだが、現代のWebアプリケーションには、それらよりも遥かに巧妙で破壊的な脅威が潜んでいる。それがSSTI(Server-Side Template Injection)だ。

「テンプレートエンジンを使っているから安全だろう」という思い込みが、バックドアを開放する。今日は、なぜSSTIがRCE(リモートコード実行)という最悪の結末を招くのか、そのメカニズムを解剖し、現場で即戦力となる防衛策を叩き込む。

—

1. SSTIの正体:なぜ「ただの文字列」が「実行権限」に化けるのか

多くのエンジニアは、Jinja2(Python)やFreeMarker(Java)、Smarty(PHP)を「ビューを綺麗に描画するためのツール」だと認識している。しかし、テンプレートエンジンは本来、「実行時に動的なコードを解釈・評価する小さなインタープリタ」だ。

攻撃者は、ユーザー入力がテンプレートの一部として結合される場所に目をつけ、テンプレートエンジンの「オブジェクト探索機能(Introspection)」を悪用する。

攻撃のメカニズム(PoC)

例えば、ユーザーの氏名を表示するだけの単純なJinja2コードを想像してほしい。

脆弱な実装例
template = “Hello, ” + user_input
return render_template_string(template)

攻撃者が user_input に {{ 77 }} を注入すると、画面には Hello, 49 と表示される。ここからが本番だ。攻撃者はPythonのオブジェクト階層を遡り、OSコマンドを実行するモジュールを呼び出す。

RCEに至るペイロードの概念例
{{ self.__init__.__globals__.__builtins__[‘__import__’](‘os’).popen(‘id’).read() }}

この一行で、サーバーのシェル権限を奪取される。アプリケーションが持つ権限=サーバーが持つ権限。これが、私がSSTIを「SQLiよりも悪質」と呼ぶ理由だ。

—

2. 実務で勝つためのセキュア実装:禁じ手と解決策

SSTIを防ぐための黄金律は、「ユーザー入力をテンプレートのソースコードとして扱わせないこと」に尽きる。

NGパターン(絶対禁止)

動的なテンプレート生成に、文字列連結やf-stringを使ってはならない。

脆弱:ユーザー入力をテンプレート文字列に埋め込んでいる
render_template_string(f”Hello, {user_name}”)

OKパターン(セキュアな設計)

テンプレートは「静的なファイル」として保持し、ユーザー入力は「コンテキストデータ」として引き渡すのが鉄則だ。

セキュア:テンプレートファイルは固定、変数は引数で渡す
template.html: Hello, {{ name }}
render_template(“template.html”, name=user_input)

この実装であれば、たとえ user_input に {{ 77 }} が含まれていても、テンプレートエンジンはそれを単なる「文字列」としてレンダリングするだけで、実行は行われない。

—

3. インフラレイヤーでの多層防御:WAFと権限管理

コードレベルの修正が間に合わない、あるいはレガシーシステムで改修が困難な場合、インフラ側で防波堤を築く必要がある。

WAFの活用(AWS WAFの例)

SSTIのペイロードには {{, }}, __globals__, class, import といった特徴的なキーワードが含まれる。AWS WAFでこれらをブロックするカスタムルールを組むのが有効だ。

/ AWS WAF用 JSON形式のルール例 /
{
“Name”: “SSTI-Block”,
“Statement”: {
“ByteMatchStatement”: {
“SearchString”: “{{“,
“FieldToMatch”: { “QueryString”: {} },
“TextTransformations”: [{ “Priority”: 0, “Type”: “URL_DECODE” }]
}
}
}

OS権限の最小化(最小権限の原則)

もしRCEが実行されても、被害を最小限に留めるために、Webプロセスを実行するユーザーは極めて限定的な権限であるべきだ。

  • Dockerの利用: アプリはルート権限で動かさない (USER 1000)。
  • ファイルシステム: テンプレートディレクトリ以外は書き込み権限を剥奪する (chmod 555)。
  • ネットワーク: アプリサーバーからインターネットへの直接のEgress通信を制限し、バックドア接続(リバースシェル)を遮断する。

—

最後に:セキュリティは「疑うこと」から始まる

私がこの仕事を続けている理由は、どんなに堅牢なフレームワークを使っても、結局は「実装者の意図しないパス」が脆弱性を生むからだ。

SSTIは、テンプレートエンジンが持つ「便利さ」という毒だ。開発チームにはこう伝えてほしい。「テンプレートを文字列として動的生成するコードは、その時点でバグである」と。

システムは、書いた通りの挙動しかしない。しかし、攻撃者は「書かなかったはずの挙動」を執拗に探している。この記事が、君たちのコードをより強固なものにする一助となれば幸いだ。現場からは以上だ。また次のインシデント……いや、次の安全な実装でお会いしよう。

コメント

タイトルとURLをコピーしました