説明
外部から制御できるテキストをJinjaのテンプレートソースに渡すと、サーバーサイドテンプレートインジェクション(SSTI)が発生するおそれがあります。攻撃者は、サーバーが評価する{{ ... }}や{% ... %}などの式や文を挿入できます。影響は、テンプレートに公開するデータ、グローバル変数、呼び出し可能なオブジェクトによって異なります。
Jinjaの自動エスケープは、出力をHTMLの文脈で保護する機能です。信頼できないテキストがテンプレートソースとして解釈されるSSTIは防げません。
想定される影響
- テンプレートに公開されたデータや設定情報が漏えいするおそれがあります。
- 公開されたオブジェクトの副作用を伴うメソッドが呼び出され、条件によってはコードの実行につながる可能性があります。
- 小さなテンプレートでも、大量の出力やCPU・メモリの過剰な消費を引き起こす可能性があります。
対処方法
- テンプレートソースは開発者が管理してください。固定テンプレートを名前やファイルで読み込み、信頼できない値はコンテキストデータとしてのみ渡してください。
- リクエストデータからテンプレートソースを組み立てたり、
render_template_string(...)やstream_template_string(...)などの文字列レンダリングAPIに渡したりしないでください。区切り文字の削除、HTMLエスケープ、名前がsanitizeというだけの関数では十分な対策になりません。 - 許可リストは、開発者が作成した完全なテンプレートから選択する用途に限定してください。任意のユーザー作成テンプレートを、文字の許可リストだけで安全にしようとしないでください。
- ユーザー作成テンプレートが必要な場合は、まず機能を制限した専用言語を検討してください。Jinjaが必要であれば、
SandboxedEnvironmentまたはImmutableSandboxedEnvironmentを使用し、副作用を伴うメソッドを含まない最小限のデータだけを公開してください。レンダリングエラーを処理し、隔離したサービスでCPU・メモリ・出力量を制限してください。Jinjaのサンドボックスだけでは完全な隔離は実現できません。
例
変更前
python
from flask import Flask, render_template_string, request
app = Flask(__name__)
@app.get("/greeting")
def bad_greeting():
return render_template_string(request.args["template"])
変更後
python
from flask import Flask, render_template, request
app = Flask(__name__)
@app.get("/greeting")
def safe_greeting():
return render_template(
"greeting.html",
name=request.args.get("name", ""),
)
templates/greeting.htmlは、アプリケーションとともに配置する固定ファイルです。
html
Hello {{ name }}
説明:
- 変更前: リクエストの値をテンプレートソースに渡すため、攻撃者がJinjaの構文を指定できます。
- 変更後: 配置済みのファイルをテンプレートソースとして使い、リクエストの値は
nameのコンテキストデータとしてのみ渡します。
テンプレートソースとデータ
source=には開発者が管理するテンプレートを渡してください。ユーザーの値をテンプレート文字列に連結せず、別のコンテキストデータとして渡してください。
python
from flask import request
from jinja2 import Environment
env = Environment()
def unsafe_keyword_source():
return env.from_string(globals={}, source=request.args["template"])
def safe_keyword_context():
return env.from_string(
globals={"name": request.args["name"]}, source="Hello {{ name }}"
)
2番目の呼び出しはテンプレート文字列を固定し、ユーザーの値をデータとして渡します。結果をHTMLとして返す場合は、自動エスケープなど出力の文脈に応じた保護も適用してください。
制約
ユーザー作成テンプレートを許可するサービスでは、サンドボックスの有無にかかわらず、公開データ、呼び出し可能なオブジェクト、例外処理、リソース制限、隔離を併せて検討する必要があります。固定テンプレートでも、HTMLなど出力の文脈に応じた保護は別途必要です。