【入門編】サーバーサイドテンプレートインジェクション(SSTI)とXSSの境界 – アプリケーションセキュリティ & 安全な開発防御ガイド

「テンプレート」が裏切る日:SSTIとXSSの境界線を、家の鍵に例えて紐解く

こんにちは。セキュリティの現場で日々、コードとログの迷宮を歩いているエンジニアです。

今日は、Web開発の現場で「便利すぎてつい油断してしまう」二つの落とし穴、XSS(クロスサイトスクリプティング)とSSTI(サーバーサイドテンプレートインジェクション)についてお話しします。

「名前が難しそう…」と思いましたか? 大丈夫です。これらは、皆さんの身近にある「家の防犯」に例えると、驚くほどスッキリ理解できます。さあ、一緒に紐解いていきましょう。

—

1. XSSとSSTI、何が違うの?(家の防犯に例えると)

まずは、イメージを固めましょう。

  • XSS(クロスサイトスクリプティング)は「泥棒の置き土産」

家のポストに怪しい手紙(スクリプト)を忍び込ませ、それを受け取った家族(ユーザー)が家の中でその手紙を読み上げた途端、家の中の貴重品が盗まれる状況です。攻撃は「家族のブラウザ」の中で起きます。

  • SSTI(サーバーサイドテンプレートインジェクション)は「合鍵を作られた状態」

これはもっと深刻です。泥棒が家の設計図を書き換えて、家そのものを操り始める状態です。「サーバーの中」で攻撃が完結するため、サーバー自体が乗っ取られるリスクがあります。

—

2. なぜ「テンプレート」が危険なのか

現代のWeb開発では、HTMLの中に変数を埋め込む「テンプレートエンジン」(Jinja2, Thymeleaf, EJSなど)がよく使われます。

例えば、ユーザーの名前を表示するこんなコードを想像してください。

Flask(Jinja2)の例:ユーザー入力をそのままレンダリング
これは絶対にやってはいけない例です!
@app.route(“/hello”)
def hello():
user_name = request.args.get(“name”)
# テンプレートエンジンにそのまま文字列を渡すと…
return render_template_string(f”

こんにちは、{user_name}さん!

“)

ここで、攻撃者が name パラメータに {{ 77 }} と入力したらどうなるでしょう? 賢いテンプレートエンジンはそれを計算して 49 と表示してしまいます。これがSSTIの入り口です。もしここが {{ self.__init__.__globals__ }} だったら? サーバーの中身が丸見えになってしまいます。

—

3. 境界線:どこまでがXSSで、どこからがSSTI?

ここが一番のポイントです。

  • XSSは「表示される側」の問題:

という文字列を、ただの「文字」として画面に出してしまえば、ブラウザがそれを実行してXSSになります。

  • SSTIは「処理する側」の問題:

{{ 77 }} を、ブラウザに届く前に「サーバーが計算・実行」してしまったら、それはSSTIです。

つまり、「サーバーがテンプレートの命令文として解釈して実行してしまったらSSTI、ブラウザがJavaScriptとして実行してしまったらXSS」なのです。

—

4. 現場でできる「鉄壁の守り方」

難しく考える必要はありません。基本ルールはたった二つです。

① 「ユーザーの入力」をテンプレートの「構造」に混ぜない

これが最大の防御です。テンプレートエンジンはあくまで「決まった枠組み(HTML)」を描画するためのもの。そこにユーザーの入力を直接埋め込んではいけません。

【修正後】変数は引数として渡す!
これならユーザーが {{ 77 }} と入力しても、ただの文字列として扱われます。
@app.route(“/hello”)
def hello():
user_name = request.args.get(“name”)
return render_template(“hello.html”, name=user_name)

② HTTPヘッダーで「ブラウザの暴走」を防ぐ

もし万が一、XSSの脆弱性が入り込んでしまっても、以下のヘッダーを設定しておけば被害を最小限に抑えられます。

  • Content-Security-Policy (CSP):

「このサイトでは、信頼できない場所からのスクリプトは絶対に実行させない!」とブラウザに命令する最強の盾です。

レスポンスヘッダーの設定例
Content-Security-Policy: default-src ‘self’; script-src ‘self’; object-src ‘none’;

  • default-src 'self':自分のサイト以外のコンテンツを読み込ませない。
  • object-src 'none':プラグイン(Flash等)の実行を無効化する。

—

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

セキュリティ対策に「完璧」はありません。しかし、「テンプレートエンジンにユーザーの入力を渡すときは、常に爆弾を扱っていると思え」という意識を持つだけで、皆さんのコードの堅牢性は劇的に向上します。

最初は失敗することもあるでしょう。でも、その一つ一つがエンジニアとしての「勘」を育ててくれます。分からなくなったら、またいつでもここに戻ってきてくださいね。

安全な開発ライフを楽しみましょう!

コメント

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