high · 7.4CVE-2026-84366Sep 2, 2026

CVE-2026-84366: Scrapy S3DownloadHandler Cleartext Transmission of AWS Credentials

Shubham Kandhare
Security Engagement Manager, SecureLayer7

Scrapy's S3 download handler sends AWS-signed requests over plain HTTP by default, exposing credentials and object contents to any network observer.

Packagescrapy
Ecosystempip
Affected< 2.17.0
Fixed in2.17
CVE-2026-84366: Scrapy S3DownloadHandler Cleartext Transmission of AWS Credentials

The problem

In scrapy/core/downloader/handlers/s3.py, the handler converts any s3:// URL into an HTTP request unless the caller explicitly sets request.meta["is_secure"]. The generated request is then signed with configured AWS credentials and sent in cleartext.

Anyone on the network path can read the AWS Authorization header, the X-Amz-Security-Token (when temporary credentials are used), the bucket and key path, and the full S3 response body. An active MITM attacker can also modify response data before Scrapy processes it, enabling scraped-data poisoning, HTTP cache poisoning, and forged redirect injection.

Proof of concept

A working proof-of-concept for CVE-2026-84366 in scrapy, with the exact payload below.

python
import scrapy
from scrapy.http import Request

# Any s3:// request WITHOUT is_secure=True triggers plaintext HTTP.
# The signed request sent on the wire looks like:
#
# GET http://my-bucket.s3.amazonaws.com/sensitive-key HTTP/1.1
# Authorization: AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/...
# X-Amz-Security-Token: FwoGZXIvYXdz...
# X-Amz-Date: 20260101T000000Z
#
# is_secure is not set, so the default branch produces scheme="http".

req = Request(
    url="s3://my-bucket/sensitive-key"
    # meta={"is_secure": True}  <-- omitting this is the vulnerability
)

The root cause is a bad default in the scheme selection expression: scheme = "https" if request.meta.get("is_secure") else "http". Because get() returns None when the key is absent, the ternary always falls through to "http" unless the caller knows to set the flag.

The signed AWS credentials are then added to a plaintext request, violating CWE-319.

The patch in commit 9523e1ec simply inverts the default, making HTTPS unconditional and removing the is_secure branch entirely. After the fix, there is no opt-in required for TLS.

The fix

Upgrade to Scrapy 2.17.0 (released 2026-07-07). The fix commit 9523e1ec8c41fde265a26d14563d178b6f1ad04b removes the is_secure branch and hard-codes HTTPS for all S3 requests. No configuration change is needed after upgrading. If you cannot upgrade immediately, set request.meta["is_secure"] = True on every s3:// Request as a temporary workaround.

Reporter not attributed.

References: [1][2][3][4][5]

Related research