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.