「テンプレート」が裏切る日: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等)の実行を無効化する。
—
最後に:セキュリティは「疑うこと」から始まる
セキュリティ対策に「完璧」はありません。しかし、「テンプレートエンジンにユーザーの入力を渡すときは、常に爆弾を扱っていると思え」という意識を持つだけで、皆さんのコードの堅牢性は劇的に向上します。
最初は失敗することもあるでしょう。でも、その一つ一つがエンジニアとしての「勘」を育ててくれます。分からなくなったら、またいつでもここに戻ってきてくださいね。
安全な開発ライフを楽しみましょう!
コメント