flawopen.com/SSTI/Jinja2
Jinja2 の render_template_string() は安全か
render_template_string() は、テンプレート文字列をアプリケーションコードが固定している場合に限って安全に使えます。Jinja が解析する前に信頼できない入力をその文字列へ埋め込むと、サーバーサイドテンプレートインジェクション(SSTI)になります。Flask の既定のサンドボックスなし Jinja 環境では、アクセス可能なオブジェクト、設定、アプリの実装次第でコード実行に至る可能性があります。すべての環境で自動的に発生するわけではありません。
# All four are the same bug render_template_string(f"Hi {name}") render_template_string("Hi " + name) render_template_string("Hi %s" % name) render_template_string("Hi {}".format(name)) # A very common real-world shape: @app.errorhandler(404) def not_found(e): return render_template_string( f"<h1>No such page: " f"{request.path}</h1>" ), 404 # The URL path is attacker-controlled.
# Template is a literal; the user value # arrives as a named context variable render_template_string( "Hi {{ name }}", name=name ) # Better still, a real template file render_template("greeting.html", name=name) # For the 404 case, no template # evaluation is needed at all: return render_template("404.html", path=request.path), 404
攻撃者がテンプレートソースを制御すると何が起きるか
Flask は既定で config、request、session などのアプリケーショングローバルをテンプレートに公開します。攻撃者がテンプレートソースを制御すると、Jinja はその環境で利用できるオブジェクトを使って式を評価します。影響はアプリが公開するオブジェクトや機能によって異なります。無害な算術式は評価の発生を確認できますが、それだけでコード実行やアクセス可能なデータを証明するものではありません。
アプリがコンテキストに含めた機密値は、テンプレートから見える可能性があります。たとえば Flask の config に SECRET_KEY やデータベース設定が含まれる場合がありますが、環境ごとに異なります。テンプレートから見えるデータに秘密情報を置かず、出力エスケープをテンプレート評価への防壁と考えないでください。
CVE-2024-56201 は別の条件で発生する問題
これは通常の SSTI とは別の Jinja コンパイラー脆弱性であり、通常の SSTI の前提条件ではありません。CVE-2024-56201 は、攻撃者がテンプレートの内容と名前の両方を制御できる場合、Jinja 3.0.0〜3.1.4 に影響します。この条件ではサンドボックスを使っていても任意の Python コード実行が可能でした。Jinja 3.1.5 で修正されています。攻撃者が指定したテンプレートや名前を受け入れる場合は、アドバイザリーを確認して更新してください。
発生しやすい箇所
リクエストパスを表示するエラーページ、メールテンプレートの管理機能、テナント別のカスタマイズ機能、公開すべきでないプレビューやデバッグ用エンドポイントなどで見られます。
よくある質問
いいえ。autoescape はレンダリングされた値をエスケープして、出力時の XSS を抑えます。ユーザー文字列がテンプレートソースとしてコンパイルされることは防ぎません。異なる処理段階への対策です。
Jinja にはユーザー作成テンプレート用の SandboxedEnvironment がありますが、公式ドキュメントも完全なセキュリティ境界ではないと説明しています。必要なデータだけを渡し、副作用のあるオブジェクトを避け、テンプレートエンジンの外側で CPU、メモリ、プロセスを制限します。解析前に信頼できない入力を埋め込む方法が安全になるわけではありません。
render_template_string を検索し、第1引数がアプリ管理の固定文字列か確認します。ユーザー値は名前付きコンテキスト変数で渡すか、テンプレートファイルを使います。テンプレートを動的にコンパイルする API と名前を制御できる経路も確認します。