テンプレートエンジンは「魔法の箱」じゃない!SSTIから身を守るための基礎知識
こんにちは!セキュリティの現場で日々、泥臭い攻防を繰り広げているエンジニアです。
今日は、Web開発で避けては通れない「テンプレートエンジン」と、そこに潜むちょっと怖い落とし穴「SSTI(Server-Side Template Injection)」についてお話しします。
「テンプレートエンジンなんて、ただの表示ツールでしょう?」と思っているなら要注意。実は、ここを適当に扱うと、あなたの家の玄関の鍵を、泥棒自身に開けさせるような事態を招いてしまうんです。
—
1. SSTIって何?身近な例で考えてみよう
まずは、SSTIを「手紙の代筆」に例えてみましょう。
あなたが「お客様への挨拶文」を自動生成するシステムを作ったとします。
テンプレートには「こんにちは、{{name}}さん!」と書いてあり、プログラム側でnameに「田中さん」を入れると、「こんにちは、田中さん!」と表示されますよね。
ここで悪意ある泥棒(攻撃者)がやってきて、nameの場所に、こんな細工を仕掛けたらどうなるでしょうか?
- 「田中さん」ではなく、「
{{system.delete_all_files()}}」という「呪文」を入力する。
もしテンプレートエンジンがこの呪文を「あ、これは命令だ!」と勘違いして実行してしまったら……はい、システムは大惨事です。これがSSTIの正体。ユーザーの入力が「データ」としてではなく「命令」として解釈されてしまう脆弱性なんです。
—
2. なぜ「サンドボックス」だけでは不十分なのか
多くのテンプレートエンジンには「サンドボックス(砂場)」という機能があります。「ここから外には出ちゃダメだよ」という遊び場です。
でも、考えてみてください。砂場の柵を乗り越える子供(攻撃者)がいたらどうしますか?
実は、テンプレートエンジンの多くは、PythonやJavaといった言語の内部構造にアクセスする「抜け道」を持っています。サンドボックスはあくまで「お行儀よく遊ぶためのルール」であって、「絶対的な檻」ではないことを理解しておく必要があります。
—
3. SSTIを防ぐ「鉄則」:ロジックとビューを切り離せ!
SSTIを防ぐための最大の秘訣は、「テンプレートの中に、計算や命令を書かせないこと」です。
悪い例:テンプレートの中でロジックを動かす
ようこそ、{{ user.name }}さん
良い例:データは「完成品」として渡す
テンプレートはあくまで「表示用」と割り切り、必要なデータはすべてプログラム側(コントローラー)で準備してから渡しましょう。
【Python (Flask/Jinja2) の例】
悪い例:テンプレートで何でもできちゃう状態
return render_template_string(f”Hello {user_input}”)
良い例:安全な実装
@app.route(“/greet”)
def greet():
# ユーザー入力をそのままテンプレートに埋め込まず、
# 処理済みの「ただの文字列」として渡すのが鉄則です!
user_name = get_sanitized_name(request.args.get(“name”))
return render_template(“greet.html”, name=user_name)
【greet.html (テンプレート側)】
ようこそ、{{ name }}さん
—
4. 開発現場で今すぐできる「防御の3ステップ」
明日からの開発で、ぜひ意識してほしいポイントをまとめました。
1. ユーザー入力を直接テンプレートの引数にしない
- テンプレートエンジンの構築メソッド(
render_template_stringなど)に、ユーザーからの入力をそのまま文字列連結で渡すのは「鍵をポストに放置する」のと同じくらい危険です。
2. テンプレート変数をエスケープする
- ほとんどのモダンなエンジン(Jinja2など)は自動でエスケープしてくれますが、設定を無効化(
|safeフィルターを多用するなど)していないか、一度見直してみましょう。
3. 「ロジックはバックエンドに隠す」
- ビュー(HTML)は「見た目」だけを整える場所。データベースの参照やファイル操作などの「重い処理」は、すべてバックエンドの関数に隠して、テンプレートには「結果の文字列」だけを渡すように設計してください。
—
まとめ:セキュリティは「疑うこと」から始まる
セキュリティは難しいと思われがちですが、根本にあるのは「自分の庭(システム)には、招かれざる客が泥棒の格好をしてやってくるかもしれない」という想像力です。
テンプレートエンジンは非常に便利ですが、その便利さの裏には「命令を実行できる」というパワーが隠されています。そのパワーをユーザーに渡さないこと。これだけで、あなたのシステムは格段に堅牢になります。
一歩ずつ、安全なコードを書いていきましょう。応援しています!
コメント