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

こんにちは!現場の最前線でセキュリティと格闘しているエンジニアの皆さん。今日は、開発の現場で意外と見落とされがちな、でも一度食らうと致命傷になる「SSTI(サーバーサイドテンプレートインジェクション)」についてお話ししますね。

「難しそう…」と身構える必要はありません。まずは身近な例から紐解いていきましょう。

—

1. SSTIってなに?泥棒の例えで考えてみよう

皆さんの家には「鍵」がありますよね。玄関の鍵を閉めておけば、知らない人が勝手に入ってくることはありません。

Web開発におけるテンプレートエンジン(Jinja2やThymeleafなど)は、「手紙のテンプレートに、宛名だけ差し替えて大量に送る」ための便利なツールです。例えば、「こんにちは、{{ 名前 }}さん!」という枠組みに、ユーザーの入力した名前を当てはめて表示する仕組みです。

ここで問題になるのは、「テンプレートの中に、宛名以外の『命令文』を書き込める隙間があるかどうか」です。

もし、泥棒が宛名の欄に「宛名」ではなく「裏口の鍵を開けろ」というメモを忍び込ませて、それを家主(サーバー)が「あ、これは宛名じゃなくて指示書なんだね」と勘違いして実行してしまったら……これがSSTIの正体です。

攻撃者は、テンプレートエンジンが持つ「計算したり、別のプログラムを呼び出したりする機能」を悪用して、サーバーを乗っ取ろうとします。

—

2. なぜ起こるのか?:プログラムの「勘違い」

開発者が「ユーザーからの入力をそのままテンプレートに埋め込む」というコードを書くと、この事故が起きます。

例えば、PythonのJinja2でこんなコードを書いていないでしょうか?

危険なコードの例
from flask import Flask, request, render_template_string

app = Flask(__name__)

@app.route(“/hello”)
def hello():
user_name = request.args.get(‘name’)
# ユーザーの入力をそのままテンプレート文字列として処理している!
template = f”

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

”
return render_template_string(template)

もし攻撃者が ?name={{ 77 }} と入力したら、画面には「49」と表示されます。これならまだ可愛いものですが、これが「システムコマンドを実行せよ」という指示にすり替わると、サーバー上のファイルが全て盗まれたり、破壊されたりするんです。

—

3. どうやって防ぐ?:泥棒を入れない対策

対策は大きく分けて2つ。「入力値を信用しないこと」と「テンプレートの仕組みを分離すること」です。

対策①:入力値は「ただのデータ」として扱う

テンプレートエンジンには、ユーザー入力を「プログラムの命令」ではなく「ただの文字列」として安全に表示する機能が備わっています。

安全なコードの例
@app.route(“/hello”)
def hello():
user_name = request.args.get(‘name’)
# テンプレート文字列の中に直接埋め込まず、変数として渡す
return render_template_string(“

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

“, name=user_name)

これなら、泥棒が {{ 77 }} と入力しても、画面にはそのまま「{{ 77 }}」と表示されるだけで、プログラムは実行されません。これが最強の防犯対策です。

対策②:サンドボックス環境の検討

もし、どうしてもユーザーにテンプレートを書かせたい特殊なアプリを作るなら、「サンドボックス」という隔離された部屋を用意します。

砂場(サンドボックス)で遊んでいる限り、その子供(ユーザー)がどれだけ暴れても、外の家財道具(OSコマンドや重要なファイル)には手が届かないという仕組みです。権限を極限まで絞った環境でテンプレートを実行するように設計しましょう。

—

4. セキュリティ担当者からのアドバイス

「テンプレートエンジンを使っているなら、まずは {{ や }} といった特殊記号が、ユーザー入力に混じっていないかチェックする」だけでも、攻撃の芽を摘むことができます。

また、WAF(Web Application Firewall)を導入して、「テンプレート特有の構文を攻撃として検知・遮断する」のも、現代の防衛戦では非常に有効です。

まとめ:一歩ずつ進んでいきましょう!

1. 「ユーザー入力」と「テンプレートの構文」を混ぜない!
2. テンプレートエンジンに値を渡すときは、専用の変数バインド機能を使う!
3. もし不安なら、まずは入力値のバリデーション(チェック)を徹底する!

セキュリティは、「一度設定して終わり」ではなく、日々の開発習慣の積み重ねです。今日から皆さんの書くコードで、「ユーザーの入力は、宛名を書く場所にだけ入れてもらう」というルールを意識してみてください。

それだけで、皆さんのサーバーはぐっと安全になります。応援していますよ!

コメント

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