JSON Injection
ID |
python.json_injection |
Severity |
high |
Resource |
Injection |
Language |
Python |
Tags |
CWE:116, NIST.SP.800-53, OWASP:2021:A3, PCI-DSS:6.5.1 |
Description
JSON Injection occurs when untrusted input is inserted into a JSON structure without proper validation or sanitization, allowing an attacker to manipulate or inject arbitrary JSON content. This can lead to unauthorized data exposure, privilege escalation, or denial of service.
Rationale
JSON Injection (CWE-116) arises when applications build JSON data structures by concatenating or formatting strings that include unsanitized user input, rather than using proper data structures or serialization methods. In Python, this frequently happens when developers manually construct JSON strings instead of using libraries like json
or jwt
safely.
Consider the following vulnerable example, where user input is directly embedded into a JSON string using string interpolation:
from flask import Flask, request, Response
app = Flask(name)
@app.route("/user")
def user():
username = request.args.get("name", "guest")
# Vulnerable: directly embedding user input into JSON string
response = f'{{"user": "{username}"}}'
return Response(response, mimetype="application/json")
If an attacker sends a specially crafted input such as:
foo", "is_admin": true, "extra": "data
The resulting JSON becomes:
{"user": "foo", "is_admin": true, "extra": "data"}
which injects unauthorized fields into the JSON response. This manipulation can bypass authorization controls or confuse JSON parsers consuming this data.
Another common scenario is with JWT tokens, where the payload is constructed insecurely from unsanitized input, allowing attackers to escalate privileges by injecting roles or flags.
Remediation
To prevent JSON Injection vulnerabilities in Python:
-
Avoid manual JSON string construction with untrusted input. Always use safe data structures like dictionaries or objects, then serialize using trusted libraries like json.dumps().
-
Validate and sanitize all user input. Ensure input conforms to expected formats before incorporating it into JSON payloads.
-
For JWT payloads, build dictionaries directly instead of parsing strings. Example:
@app.route("/token")
def token():
username = request.args.get("name", "guest")
payload = {"user": username}
tkn = jwt.encode(payload, SECRET, algorithm="HS256")
return jsonify({"token": tkn})