Overview
Unrestricted Resource Consumption (URC) in Flask arises when endpoints allow requests to trigger unbounded or excessively heavy work, consuming CPU, memory, or I/O resources and thereby degrading or taking the service offline. In real Flask APIs, this may happen if input directly drives loops, expensive computations, or large payload processing with no upper bounds, timeouts, or rate controls. When attackers can craft requests that force such workloads, a single or small set of endpoints can be driven to exhaustion under concurrent load, leading to Denial of Service (DoS) conditions or degraded performance for legitimate users. The provided CVE note (CVE-2021-21241) concerns Flask-Security-Too behavior where token exposure occurred in GET responses for certain endpoints; while not a URC vulnerability per se, it highlights the risk of unprotected endpoints leaking sensitive data, which can indirectly exacerbate resource abuse if tokens are misused to trigger additional requests. This guides us to harden endpoints, enforce rate limits, and upgrade components to patched versions, which aligns with URC mitigation in Flask applications.
Affected Versions
Flask-Security-Too: 3.3.0 <= version < 3.4.5 (i.e., 3.3.0-3.4.4); patched in 3.4.5 and 4.0.0
Code Fix Example
Flask API Security Remediation
# VULNERABLE
from flask import Flask, request, jsonify
app = Flask(__name__)
def heavy_work(n: int) -> int:
s = 0
for i in range(n):
s += (i * i) % 97
return s
@app.route('/vulnerable/process')
def vulnerable_process():
# Unbounded workload driven by user input
n = int(request.args.get('n', '100000')) # attacker can choose a huge n
result = heavy_work(n)
return jsonify({"result": result})
# FIX
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
app.config["MAX_CONTENT_LENGTH"] = 1 * 1024 * 1024 # 1 MB payload limit
limiter = Limiter(app, key_func=get_remote_address)
@app.route('/fixed/process')
@limiter.limit("200 per hour")
def fixed_process():
n = int(request.args.get('n', '1000'))
if n > 10000:
return jsonify({"error": "n too large"}), 400
result = heavy_work(n)
return jsonify({"result": result})
if __name__ == '__main__':
app.run()