See how tornadoweb compares to other vendors in security performance
In versions of Tornado prior to 6.5.5, the only limit on the number of parts in multipart/form-data is the maxbodysize setting (default 100MB). Since parsing occurs synchronously on the main thread, this creates the possibility of denial-of-service due to the cost of parsing very large multipart bodies with many parts.
Tornado 6.5.5 introduces new limits on the size and complexity of multipart bodies, including a default limit of 100 parts per request. These limits are configurable if needed; see tornado.httputil.ParseMultipartConfig. It is also now possible to disable multipart/form-data parsing entirely if it is not required for the application.
Last updated 11 December 2024
Summary
When Tornado's multipart/form-data parser encounters certain errors, it logs a warning but continues trying to parse the remainder of the data. This allows remote attackers to generate an extremely high volume of logs, constituting a DoS attack. This DoS is compounded by the fact that the logging subsystem is synchronous.
Affected versions
All versions of Tornado prior to 6.5 are affected. The vulnerable parser is enabled by default.
Solution
Upgrade to Tornado version 6.5. In the meantime, risk can be mitigated by blocking Content-Type: multipart/form-data in a proxy.
Summary
The HTTPHeaders.add method in Tornado accumulates values using string concatenation when the same header name is repeated. Due to Python string immutability, each concatenation copies the entire string, resulting in O(n²) time complexity.
Given Tornado's single event loop architecture, a single maliciously crafted HTTP request can block the server's event loop for an extended period, causing a Denial of Service (DoS).
Severity: High if maxheadersize has been increased from its default, low if it has its default value of 64KB.
Summary
The parseparam function in Tornado's httputil.py is used to parse specific HTTP header values, such as those in multipart/form-data. This function uses an inefficient algorithm that repeatedly calls string.count() within a nested loop while processing quoted semicolons (e.g., param=";").
As a result, if an attacker sends a request with a large number of maliciously crafted parameters in a Content-Disposition header, the server's CPU usage increases quadratically (O(n²)) during parsing. Due to Tornado's single event loop architecture, a single malicious request can cause the entire server to become unresponsive for an extended period, leading to a Denial of Service (DoS).
Severity: High
CRLF injection vulnerability in the tornado.web.RequestHandler.setheader function in Tornado before 2.2.1 allows remote attackers to inject arbitrary HTTP headers and conduct HTTP response splitting attacks via crafted input.
In Tornado before 6.5.5, cookie attribute injection could occur because the domain, path, and samesite arguments to .RequestHandler.setcookie were not checked for crafted characters.
From http://www.tornadoweb.org/en/stable/releases/v3.2.2.html
Security fixes
The XSRF token is now encoded with a random mask on each request. This makes it safe to include in compressed pages without being vulnerable to the BREACH attack. This applies to most applications that use both the xsrfcookies and gzip options (or have gzip applied by a proxy).
Backwards-compatibility notes
If Tornado 3.2.2 is run at the same time as older versions on the same domain, there is some potential for issues with the differing cookie versions. The Application setting xsrfcookieversion=1 can be used for a transitional period to generate the older cookie format on newer servers.
Upstream patch: https://github.com/tornadoweb/tornado/commit/1c36307463b1e8affae100bf9386948e6c1b2308
Last updated 11 December 2024
Header injection and XSS via reason argument
Summary
The reason argument (used by both RequestHandler.setstatus and tornado.web.HTTPError is designed to allow applications to pass custom "reason" phrases (the "Not Found" in HTTP/1.1 404 Not Found) to the HTTP status line (mainly for non-standard status codes). Vulnerabilities exist in Tornado versions prior to 6.5.3 if untrusted data is passed as the reason argument.
Details
In vulnerable versions, the supplied reason phrase is used unescaped in HTTP headers (where it could be used for header injection) or in HTML in the default error page (where it could be used for XSS).
Impact
Type: Reflected Cross-Site Scripting (CWE-79) in default error page, or header injection (CWE-644) Actors: Remote attacker who can cause the application to raise HTTPError/setstatus with an attacker-controlled reason (e.g., via query parameter used by developer). Effect: Execution of arbitrary JavaScript in victims' browsers when they view the error page — possible session token theft, CSRF escalation, UI spoofing, or other client-side attacks depending on context. Scope: Only applications that explicitly reflect untrusted input into reason are affected.
Mitigation
Aside from upgrading to Tornado 6.5.3 or newer, the vulnerability can be mitigated by not using untrusted data for the reason argument. In the intended use case the reason argument would generally be a string literal and not derived from user input. Also, the reason argument is rarely required (reason phrases are not used at all in HTTP/2) and can generally be omitted.
For a general-purpose error message in HTTPError, consider using the logmessage argument instead of reason.