Open actionable advisories and recorded vendor fixes. This public package view has no inventory attached and cannot establish whether your deployment is affected. A vendor fix never resolves your finding; verify your installed version and the source guidance.
Open advisories
26 actionable advisories, newest first. Withdrawn advisories and news are excluded. Coverage is partial.
When Tornado reads an HTTP/1.1 body that uses chunked transfer encoding, it processes all chunks that have already arrived without yielding to the event loop. A client can send a body made of millions of 1-byte chunks, which blocks the event loop (and therefore every other connection served by that process) for the entire time it takes to parse the body. A body near the default `max_body_size` of 100 MB can block the process for close to a minute, and the attacker can repeat or parallelize these requests. Bodies made of tiny chunks also consumed far more memory than their size: a handler that does not use `@stream_request_body` stored each chunk as a separate object, using roughly 20 times as much memory as the bytes received. Impact All Tornado HTTP servers are affected when they receive a request body for a handler that reads it, which includes any handler that does not reject the request before the body is read. No authentication is required. Lowering…
Recommended action: Upgrade to 6.6 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
`tornado.web.RedirectHandler` builds its target URL by substituting parts of the request path into a template. Depending on where those substitutions appear in the template, an attacker can craft a request that redirects to a host of their choosing. This creates an open redirect on the application's domain, which can be used in phishing. Impact Applications are affected if they use `RedirectHandler` with a URL template where a regex capture group can influence the scheme or host of the redirect. This includes: - A substitution at the start of the path, for example `(r"/(.*)/target", RedirectHandler, {"url": "/{0}/destination"})`. A request to `https://example.com//evil.com/target` redirects to `//evil.com/destination`, a protocol-relative URL pointing to evil.com. Variants using backslashes or encoded characters have the same effect, because browsers treat them as equivalent to `//`. - A substitution directly after the host, for example `(r"/old(.*)",…
Recommended action: Upgrade to 6.6 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Tornado's HTTP header parser takes quadratic time (O(n²)) when a header value is continued across many "obsolete line folding" (obs-fold) lines. An unauthenticated attacker can send a request whose header block is mostly short continuation lines. Parsing it uses a large amount of CPU on the IOLoop thread and blocks every other connection that process is serving. This issue is closely related to CVE-2025-67725 (GHSA-c98p-7wgm-6p64). The fix for that advisory covered repeated headers but not line-folded continuations. Impact All Tornado HTTP servers are affected. Header parsing runs before any application code, so no authentication or particular handler is required. How bad it gets depends on `max_header_size`: - Default (64 KB): each malicious request blocks the event loop for tens of milliseconds. Repeated requests can degrade service. - Raised `max_header_size`: the cost grows with the square of the header size. A single 1 MB request can block the process for…
Recommended action: Upgrade to 6.6 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
HTTPServerRequest.init in tornado/httputil.py parses the URL query string via parseqsbytes() with no field-count limit — while the sibling POST-body parsing path (parsebodyarguments) received a maxnumfields=1000 cap added earlier in this exact same release (v6.5.8, commit 8d6363ed), explicitly to bound parsing cost for the identical underlying primitive. This leaves the query-string path with the resource-exhaustion exposure the body-path fix was meant to close. File: tornado/httputil.py, line 553 (HTTPServerRequest.init) Root Cause Compare with the POST-body path fixed one commit earlier in the same release: Both call sites funnel through the same tornado.escape.parseqsbytes (a thin wrapper over urllib.parse.parseqs), which is exactly why maxnumfields was added to urllib.parse.parseqsl upstream — to let frameworks bound field count. The fix was applied only to the body path; the query-string path was missed. The request line + headers together are capped at maxheadersize…
Recommended action: Upgrade to 6.5.9 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
An unbounded memory accumulation (decompression bomb) in tornado.curlhttpclient.CurlAsyncHTTPClient — the client-side sibling gap of CVE-2026-49855 — verified end-to-end on the 2026-08-15 master snapshot (6.6.dev1) and present unchanged in the latest release tag v6.5.8 and on master (checked 2026-08-17). When a Tornado application configures the curl client (the documented deployment for proxy support / advanced TLS options) and fetch()es an attacker-chosen or attacker-compromised URL with default decompressresponse=True, a malicious server replying Content-Encoding: gzip with a ~2.8 MB wire bomb drove the client's RSS from 30,884 kB to 1,032,100 kB (~1008 MB) in 3.18 s (~350 MB/s, monotonic, no plateau) until the kernel OOM-killed the process (exit 137, cgroup OOMKilled=true) — with the transfer only 67% complete and no client-side size check ever intervening: curlhttpclient.py contains zero occurrences of maxbodysize/MAXFILESIZE. The identical bomb against the default…
Recommended action: Upgrade to 6.5.9 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
StaticFileHandler allows an unauthenticated attacker to read arbitrary files from the server's filesystem by requesting a path that resolves to a symbolic link placed inside the static root directory. Any application that serves user-uploadable content, or whose static directory is populated by a build/deploy pipeline that creates symlinks (e.g. npm link, webpack, Docker volume mounts, CDN sync tools), is affected. An attacker who can trigger the creation of a symlink pointing outside the static root—or exploit one that already exists—can retrieve sensitive files such as /etc/passwd, private keys, configuration files, or application secrets. Details The vulnerability is in tornado.web.StaticFileHandler, specifically in the interaction between two methods in tornado/web.py: - getabsolutepath uses os.path.abspath() to resolve the requested path: - validateabsolutepath uses the same os.path.abspath() on the root, then performs a string prefix check: os.path.abspath() normalises…
Recommended action: Upgrade to 6.5.9 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Tornado parses application/x-www-form-urlencoded bodies with urllib.parse.parseqs and does not pass maxnumfields. A body made almost entirely of separators produces tens of millions of fields, and the parse happens on the event loop before the handler runs, so a single request stalls the whole server. Where it is tornado/escape.py, at HEAD e530031405e2154654dedc4c84d5656b557ea310: maxnumfields is the parameter CPython added for exactly this, and it is absent. The path to it is entirely server-side and pre-dispatch. RequestHandler.execute parses the body at tornado/web.py:1821, which reaches HTTPServerRequest.parsebody at tornado/httputil.py:636, and the urlencoded branch of parsebodyarguments calls parseqsbytes at tornado/httputil.py:1030. The size that reaches it is bounded only by the body cap, which defaults to the stream's maxbuffersize of 104857600 at tornado/iostream.py:239, applied as the request body default at tornado/http1connection.py:136-140. A 100 MB body of…
Recommended action: Upgrade to 6.5.8 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Summary parsemultipartformdata (httputil.py:34) calls data.split(b"--"+boundary+b"\r\n") before the maxparts check (:35). A 600KB body with 100k parts creates a 100k-element transient list first, then rejects transient memory amplification (each split element is a copy). Pre-auth HTTP DoS. Root cause PoC gist: https://gist.github.com/afldl/649861f25d39b53b7edbe0298e171617 poc.py + output.txt (100k parts from 600KB transient list). Fix Count separators without materializing the list (e.g. data.count(b"--"+boundary) first). Credit Reported by afldl, 2026-07.
Recommended action: Upgrade to 6.5.8 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
The CVE-2026-35536 fix added a validation loop that rejects [\x00-\x20\x3b\x7f], but only for the hardcoded lowercase keys name/domain/path/samesite. The still-live deprecated kwargs path writes attacker-supplied attribute values straight into the Morsel with no validation, and because Morsel.setitem is case-insensitive, a capitalized kwarg (Domain=, Path=, SameSite=, Max-Age=) routes to the same reserved attribute while bypassing the loop — re-opening ;-delimited attribute injection. The patch's regression test (SetCookieForbiddenCharHandler) only exercises the four named params, never the kwargs path, so the gap is not regression-covered. Affected code - tornado/web.py → RequestHandler.setcookie: the validation loop covers only the lowercase named args; the trailing if kwargs: loop does morsel[k] = v with no character validation. Steps to reproduce GET /upper (uses Domain= kwarg) emits Set-Cookie: cupper=v; Domain=evil.com; Secure; SameSite=None; Path=/; GET /lower (uses…
Recommended action: Upgrade to 6.5.8 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
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
Recommended action: Upgrade to 6.5.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
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.
Recommended action: Upgrade to 6.5.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
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 —…
Recommended action: Upgrade to 6.5.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
CurlAsyncHTTPClient leaks per-request credentials on handle reuse Summary CurlAsyncHTTPClient pools and reuses pycurl handles across requests but does not reset them between requests, and several per-request options are applied with no clearing branch. As a result, sensitive state set by one request persists onto a later request on the same client that does not set it. Two credential vectors are demonstrated below — a client TLS certificate (SSLCERT/SSLKEY) and proxy basic-auth credentials (PROXYUSERPWD) — both leaking to a different, unintended host. This affects all released versions through 6.5.6. Details In tornado/curlhttpclient.py, handles are created once and returned to a free list for reuse (processqueue pops the handle at line 200, finish re-appends it at line 245), and curlsetuprequest is never preceded by curl.reset(). The function clears some carried-over state on the reused handle — unsetopt(PROXYUSERPWD) in the no-proxy branch (line 394), unsetopt(USERPWD) when no auth…
Recommended action: Upgrade to 6.5.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
When SimpleAsyncHTTPClient follows a 3xx redirect, it shallow-copies the original HTTPRequest, rewrites the URL, decrements maxredirects, and removes only the Host header. It does not clear Authorization, authusername, authpassword, or authmode when the redirect target changes origin. As a result, credentials intended for one origin can be forwarded to a different origin when followredirects=True, which is the default. Beginning in Tornado 6.5.6, SimpleAsyncHTTPClient matches the default behavior of libcurl (and therefore CurlAsyncHTTPClient): When a redirect changes the scheme, host, or port of the url, the Authorization and Cookie headers will be removed when following the redirect.
Recommended action: Upgrade to 6.5.6 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Tornado's gzip decompression routines work in limited-size chunks, but have no overall limit for the total size of decompressed chunks that they will accumulate (There has always been a limit for the total compressed size). This allows a malicious server to consume effectively unlimited amounts of memory if it is accessed via SimpleAsyncHTTPClient in its default configuration. HTTPServer is not affected in its default configuration, but it is if decompressrequest=True is set. This bug is fixed in Tornado 6.5.6. maxbodysize is now checked both for the compressed and cumulative decompressed size of the response. Prior to upgrading, this issue can be mitigated by setting decompressresponse=False or using CurlAsyncHTTPClient.
Recommended action: Upgrade to 6.5.6 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Tornado's optional native extension tornado.speedups implements websocketmask without validating that the mask argument is exactly four bytes long. The C function reads four bytes from mask unconditionally, even when Python passes a shorter byte string. This can read beyond the provided buffer, exposing up to 3 bytes of uninitialized memory. The behavior is reachable from Tornado's XSRF token decoder when xsrfcookies=True and the native extension is active. Mitigations This bug is fixed in Tornado 6.5.6. Prior to upgrading to this version, setting the environment variable TORNADOEXTENSION=0 will disable the vulnerable code (at the expense of reducing websocket performance).
Recommended action: Upgrade to 6.5.6 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
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.
Recommended action: Upgrade to 6.5.5 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Values passed to the domain, path, and samesite arguments of RequestHandler.setcookie were not completely validated in versions of Tornado prior to 6.5.5. In particular, semicolons would be allowed, which could be used to inject attacker-controlled values for other cookie attributes.
Recommended action: Upgrade to 6.5.5 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
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.
Recommended action: Upgrade to 6.5 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
The algorithm used for parsing HTTP cookies in Tornado versions prior to 6.4.2 sometimes has quadratic complexity, leading to excessive CPU consumption when parsing maliciously-crafted cookie headers. This parsing occurs in the event loop thread and may block the processing of other requests. See also CVE-2024-7592 for a similar vulnerability in cpython.
Recommended action: Upgrade to 6.4.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Tornado’s curlhttpclient.CurlAsyncHTTPClient class is vulnerable to CRLF (carriage return/line feed) injection in the request headers. Details When an HTTP request is sent using CurlAsyncHTTPClient, Tornado does not reject carriage return (\r) or line feed (\n) characters in the request headers. As a result, if an application includes an attacker-controlled header value in a request sent using CurlAsyncHTTPClient, the attacker can inject arbitrary headers into the request or cause the application to send arbitrary requests to the specified server. This behavior differs from that of the standard AsyncHTTPClient class, which does reject CRLF characters. This issue appears to stem from libcurl's (as well as pycurl's) lack of validation for the HTTPHEADER option. libcurl’s documentation states: The headers included in the linked list must not be CRLF-terminated, because libcurl adds CRLF after each header item itself. Failure to comply with this might result in strange behavior.…
Recommended action: Upgrade to 6.4.1 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
When Tornado receives a request with two Transfer-Encoding: chunked headers, it ignores them both. This enables request smuggling when Tornado is deployed behind a proxy server that emits such requests. Pound) does this. PoC 0. Install Tornado. 1. Start a simple Tornado server that echoes each received request's body: 2. Send a valid chunked request: 3. Observe that the response is as expected: 4. Send a request with two Transfer-Encoding: chunked headers: 5. Observe the strange response: This is because Tornado believes that the request has no message body, so it tries to interpret 1\r\nZ\r\n0\r\n\r\n as its own request, which causes a 400 response. With a little cleverness involving chunk-exts, you can get Tornado to instead respond 405, which has the potential to desynchronize the connection, as opposed to 400 which should always result in a connection closure. Impact Anyone using Tornado behind a proxy that forwards requests containing multiple Transfer-Encoding: chunked…
Recommended action: Upgrade to 6.4.1 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Tornado interprets -, +, and in chunk length and Content-Length values, which are not allowed by the HTTP RFCs. This can result in request smuggling when Tornado is deployed behind certain proxies that interpret those non-standard characters differently. This is known to apply to older versions of haproxy, although the current release is not affected. Details Tornado uses the int constructor to parse the values of Content-Length headers and chunk lengths in the following locations: tornado/http1connection.py:445 tornado/http1connection.py:621 tornado/http1connection.py:671 Because int("00") == int("+0") == int("-0") == int("0"), using the int constructor to parse and validate strings that should contain only ASCII digits is not a good strategy.
Recommended action: Upgrade to 6.3.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Open redirect vulnerability in Tornado versions 6.3.1 and earlier allows a remote unauthenticated attacker to redirect a user to an arbitrary web site and conduct a phishing attack by having user access a specially crafted URL.
Recommended action: Upgrade to 6.3.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Tornado before 3.2.2 sends arbitrary responses that contain a fixed CSRF token and may be sent with HTTP compression, which makes it easier for remote attackers to conduct a BREACH attack and determine this token via a series of crafted requests.
Recommended action: Upgrade to 3.2.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.