Format string injection

External input used as a format string

Description

Passing externally controlled text as the format string to str.format, str.format_map, or the standard string.Formatter lets an attacker define replacement fields. Python's brace-format syntax does not execute arbitrary Python expressions, but it can traverse attributes and indexes of the arguments or mapping values supplied to it.

Potential impact

  • Internal values reachable through format arguments or mappings may be exposed.
  • The structure of log messages or responses may be altered.
  • Invalid fields or format specifications may raise exceptions.

Remediation

  • Keep the format string constant. Pass untrusted values only as positional or keyword arguments, or as mapping values for format_map.
  • If multiple messages are needed, select a server-controlled constant template from a finite allow-list.
  • To render untrusted text literally with standard brace formatting, double both { and } before formatting. Prefer a fixed template where possible.

Examples

Before

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

After

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

Explanation:

  • Before: User input controls replacement fields and attribute/index access. For example, {user[name]} reveals a value inside the mapping.
  • After: The template is constant, and user input is only a mapping value.

Usage considerations

  • Separate external input from format strings and manage template allow-lists in trusted code.
  • When escaping braces, handle both { and }.
  • % formatting and other template engines have different syntax and safety requirements. Handle input according to the API in use.

References