Spring4shell in Flask with Bearer Tokens
Spring4shell in Flask with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Spring4shell (CVE-2022-22965) exploits a flaw in Spring Framework’s data binding and class resolution when handling multipart/form-data or JSON payloads. In a Flask service that accepts Bearer Tokens in request headers, the presence of the header itself does not prevent an attacker from sending crafted Content-Type values and parameter names that trigger remote code execution on the underlying Java runtime if Spring is present downstream. A common misconfiguration is to treat the Authorization header as a gatekeeper while allowing unchecked parameter names and file uploads to reach Spring-based microservices or internal proxies. When a Flask app proxies requests or shares infrastructure with Spring components, an attacker can send a malicious Content-Type (e.g., application/x-www-form-urlencoded with nested ${} expressions) and a Bearer Token that appears valid but does not restrict parameter processing. The token may grant access to an endpoint, but the vulnerability is in how parameter binding is handled after authentication, not in token validation. This combination exposes a path where authenticated requests (with a Bearer Token) still reach vulnerable Spring code, enabling property access or command execution. The scanner’s Authentication check would flag missing enforcement of secure token binding to parameter handling, while the BOLA/IDOR and Property Authorization checks highlight lack of per-parameter authorization and exposure of internal object graphs. Input Validation and SSRF checks further surface the risk of uncontrolled Content-Type and parameter names reaching backend services.
Bearer Tokens-Specific Remediation in Flask — concrete code fixes
To mitigate risks when using Bearer Tokens in Flask, enforce strict token validation, avoid passing raw headers into parameter-binding logic, and isolate token verification from object mapping. Below are concrete, working examples that demonstrate secure handling.
Example 1: Validate Bearer Token before parameter binding
from flask import Flask, request, jsonify
import re
app = Flask(__name__)
VALID_TOKENS = {"s3cr3tT0k3nX", "aBc123ValidToken"} # in practice use a secure vault
def verify_bearer_token(req):
auth = req.headers.get("Authorization", "")
if not auth.lower().startswith("bearer "):
return None
token = auth.split(" ", 1)[1].strip()
if token in VALID_TOKENS:
return token
return None
@app.before_request
def require_auth_for_protected_routes():
if request.path.startswith("/api/"):
token = verify_bearer_token(request)
if token is None:
return jsonify({"error": "unauthorized", "message": "valid Bearer Token required"}), 401
@app.route("/api/data", methods=["POST"])
def handle_data():
# Safe: token verified, proceed with sanitized input
payload = request.get_json(silent=True) or {}
# Perform business logic with validated data
return jsonify({"status": "ok", "token": "REDACTED"})
if __name__ == "__main__":
app.run()
Example 2: Strict header parsing and no direct use of raw header in object binding
from flask import Flask, request, jsonify
import cgi
app = Flask(__name__)
@app.route("/api/upload", methods=["POST"])
def upload_file():
# Validate Content-Type to avoid parameter-name-based injection
content_type = request.headers.get("Content-Type", "")
if not content_type.startswith("application/json"):
return jsonify({"error": "invalid content type"}), 400
# Verify Bearer Token separately
auth = request.headers.get("Authorization", "")
if not auth.lower().startswith("bearer ") or not validate_token(auth.split(" ", 1)[1].strip()):
return jsonify({"error": "invalid token"}), 401
data = request.get_json()
# Ensure no dynamic evaluation of parameter names
safe_process(data)
return jsonify({"result": "processed"})
def validate_token(token: str) -> bool:
# Replace with secure token introspection/validation
return token in {"s3cr3tT0k3nX"}
def safe_process(data):
# Avoid binding to internal object graphs that may be exposed via $ref or nested properties
if isinstance(data, dict):
for k, v in data.items():
if isinstance(k, str) and re.search(r"\$\{[^}]*\}", k):
raise ValueError("unsafe parameter name")
# Continue safe processing
Key practices shown: validate the Bearer Token early and independently of parameter parsing; reject non-JSON Content-Type for JSON endpoints; avoid passing raw headers into frameworks that perform automatic binding (e.g., via request.json or request.form); sanitize parameter names to block ${} patterns used in injection attacks; and keep token validation logic separate from business-logic binding. These steps reduce the attack surface where a Bearer Token grants access but does not implicitly protect against malformed or malicious payloads reaching vulnerable binders.