フォーマット文字列の挿入

外部入力をフォーマット文字列として使用

説明

外部から制御できる文字列を str.format、str.format_map、標準の string.Formatter のフォーマット文字列として渡すと、攻撃者が置換フィールドを指定できます。Pythonの波括弧による書式指定では任意のPython式は実行できませんが、引数やマッピングで渡された値の属性やインデックスをたどることができます。

想定される影響

  • フォーマット引数やマッピングから参照できる内部の値が漏えいするおそれがあります。
  • ログや応答メッセージの出力構造を変更される可能性があります。
  • 不正なフィールドや書式指定で例外が発生することがあります。

対処方法

  • フォーマット文字列は定数にし、信頼できない値は位置引数、キーワード引数、または format_map のマッピングの値としてのみ渡します。
  • 複数のメッセージが必要な場合は、サーバーで管理する定数テンプレートを有限の許可リストから選びます。
  • 信頼できない文字列を波括弧による書式指定でそのまま表示する場合は、処理前に { と } の両方を二重にします。可能であれば固定テンプレートを優先してください。

例

変更前

python
template = request.args.get("template")
return template.format_map({"user": {"name": "alice"}})

変更後

python
user = request.args.get("user")
return "hello {user}".format_map({"user": user})

解説:

  • 変更前: 入力によって置換フィールドや属性・インデックスへのアクセスが決まるため、{user[name]} などでマッピング内の値を表示できます。
  • 変更後: テンプレートは定数で、入力はマッピングの値としてのみ渡されます。

使用時の注意点

  • 外部入力とフォーマット文字列を分離し、テンプレートの許可リストは信頼できるコードで管理します。
  • 波括弧をエスケープする場合は、{ と } の両方を処理します。
  • % による書式指定や別のテンプレートエンジンは構文と安全条件が異なるため、使用するAPIに合わせて入力を扱います。

参考資料