Tough Cookie Prototype Pollution CVE-2023-26136: Fix 4.1.3 and Remediation

  • Thread Author
Salesforce’s widely used Node.js cookie library tough-cookie was found to contain a prototype pollution vulnerability (CVE‑2023‑26136) that affects every release before 4.1.3 when a CookieJar is created with the option rejectPublicSuffixes=false; the flaw allows specially crafted cookie domains (for example Domain=[B]proto[/B]) to leak or modify properties on JavaScript’s object prototype, and a stable fix was published in version 4.1.3.

Cracked code jar with a ghost cookie, warning CVE-2023-26136 for a JavaScript prototype issue.Background​

tough-cookie is a small but foundational library that implements RFC‑6265 cookie parsing, serialization, and a CookieJar abstraction for Node.js applications. It is commonly embedded inside HTTP clients, scrapers, automation tools, and many server-side integrations that need cookie handling. While the package itself is compact, libraries that depend on it inherit its behavior, making a flaw in cookie storage potentially far-reaching across the Node ecosystem.
The vulnerability was publicly disclosed in mid‑2023 and recorded in standard vulnerability databases; maintainers published a targeted fix and a release (v4.1.3) addressing the problem. The public advisories and vulnerability trackers consistently identify the trigger condition as using a CookieJar with rejectPublicSuffixes=false and cookies whose domain components are crafted to target object prototype keys.

What happened — a plain‑English explanation​

At the core of the issue is a common JavaScript pitfall: plain object literals ({}) inherit from Object.prototype. If an application stores untrusted keys directly on such an object, an attacker who can control those keys can write to special properties like [B]proto[/B], constructor, or prototype, thereby modifying the prototype used by many objects — this is prototype pollution.
In tough-cookie’s memory cookie store, the code created nested plain objects to index cookies by domain and path. When CookieJar operates with rejectPublicSuffixes=false, the library accepts cookies with public-suffix-like domains; an attacker who can get a victim to accept or process a cookie whose domain is [B]proto[/B] or another prototype name can cause the CookieJar implementation to assign properties into Object.prototype. Because those properties are then visible from otherwise independent objects, application code that later reads from or writes to those names can behave in unexpected and possibly dangerous ways. The original issue report and public proof‑of‑concept show exactly this pattern.

Technical anatomy — where the code went wrong​

The vulnerable pattern​

The vulnerable store used nested plain objects to represent the cookie index:
  • this.idx = {} — top-level domain map
  • this.idx[cookie.domain] = {} — per-domain path map
  • this.idx[cookie.domain][cookie.path] = {} — per-path cookie map
Assigning this.idx[cookie.domain] = {} is dangerous if cookie.domain equals [B]proto[/B] (or similar), because this.idx.[B]proto[/B] references the global prototype slot. Setting that slot effectively writes into Object.prototype, which is shared across the process. The original issue reporter included a short proof‑of‑concept demonstrating how setting a cookie with Domain=[B]proto[/B] creates a seemingly innocuous cookie entry while simultaneously causing a second object to gain new properties.

Why rejectPublicSuffixes=false matters​

The rejectPublicSuffixes option controls whether cookies with domains that are public suffixes (for example, .com, .co.uk, or other registry-controlled domains) are rejected. When this option is set to false, the CookieJar is more permissive and will accept cookies that would otherwise be rejected. Attackers can abuse this permissive setting to register or inject cookies with unusual domain strings, including the special tokens used to target prototypes. Several advisories and the upstream issue make clear that the exploit requires this permissive configuration.

The fix (high level)​

The maintainers fixed the issue by changing how internal maps were created so they no longer inherit from Object.prototype. The straightforward hardening is to allocate objects with no prototype: Object.create(null). That change isolates stored keys from JavaScript’s prototype chain and prevents prototype slots like [B]proto[/B] from being hijacked via cookie domains. The corrected logic was released in v4.1.3.

Proof‑of‑Concept and exploitability​

A public proof‑of‑concept (PoC) demonstrates the attack pattern in a few lines: set a cookie with Domain=[B]proto[/B] using a CookieJar created with rejectPublicSuffixes=false, then observe that an independent plain object now appears to have properties it should not. The PoC is simple, deterministic, and automatable, which raises the real‑world risk profile for applications that expose cookie ingestion from untrusted sources (for example, proxied HTTP responses, user‑supplied headers, or forged requests).
Exploit complexity is low: no special privileges are required, and the attacker does not need human interaction once they can ensure a vulnerable CookieJar instance processes the malicious cookie. The biggest practical hurdle is finding an attacker-controlled channel to inject such a cookie into the target application’s cookie processing path. For some deployments — proxies, web scrapers, headless browser automation, or misconfigured backends that accept arbitrary Set‑Cookie values — that channel exists. Scanners and vulnerability trackers rate the impact as medium overall (CVSS 3.1: 6.5), with some vendor advisories flagging higher potential impact when combined with downstream issues.

Risk assessment — what prototype pollution can lead to here​

Prototype pollution is a meta vulnerability: by itself it alters object properties, but the real damage depends on how application code uses those properties. Typical follow‑on risks include:
  • Data integrity issues — polluted prototypes can change behavior of utility code that iterates or expects certain keys to be absent.
  • Privilege escalation or logic bypasses — if code relies on object structure for authorization checks, prototype properties can subvert those checks.
  • Denial of service — unexpectedly mutated prototypes may cause crashes or infinite loops in some code paths.
  • Remote code execution (RCE) — while prototype pollution does not equate to RCE directly, in some complex applications polluted properties can be chained into code execution (for example, if polluting a function pointer, or influencing deserialization logic).
Because exploitation requires only that the CookieJar instance receive the crafted cookie, services that parse or accept cookies on behalf of other users — especially proxies, scraping platforms, or middleware running multiple tenants — are the most exposed. The upstream advisories and security trackers therefore recommend treating this as a moderate but actionable supply‑chain risk: upgrade dependencies and audit cookie ingestion surfaces.

Who is affected​

  • Any Node.js application that includes a vulnerable tough-cookie version (< 4.1.3) and uses CookieJar instances created with rejectPublicSuffixes=false is directly affected.
  • Indirectly affected projects include any dependencies that vendor or depend on tough-cookie; package managers and enterprise distributions that ship the vulnerable version should update their package builds.
  • Containerized services, web proxies, headless browsing automation, and server‑side scrapers are examples of components where CookieJar instances may be exposed to untrusted or attacker-controlled cookie inputs.
Multiple OS and package advisory feeds (including NVD, Snyk, GitHub Advisory Database, and various Linux distribution notices) have cataloged the issue and flagged the fix in 4.1.3; downstream vendors such as Debian, Fedora and enterprise product teams have tracked the issue and produced their own advisories for affected packages.

Practical remediation checklist — step by step​

  • Inventory
  • Search code repositories for direct dependencies on tough-cookie and for transitive references in lockfiles (package-lock.json, yarn.lock, pnpm-lock.yaml).
  • Scan containers and images for installed tough-cookie packages.
  • Upgrade
  • For direct dependencies, update to at least tough-cookie@4.1.3. Example:
  • npm: npm install --save-exact [email]tough-cookie@4.1.3[/email]
  • yarn: yarn add [email]tough-cookie@4.1.3[/email] --exact
  • For transitive dependencies, update the top‑level dependency that brings in tough-cookie or use tooling (npm/yarn resolutions, selective upgrades) to force the patched version.
  • Temporary mitigations (only if you cannot immediately upgrade)
  • Ensure CookieJar instances do not use rejectPublicSuffixes=false. Prefer the default true behavior where possible.
  • Sanitize or validate incoming Set‑Cookie headers, rejecting cookies whose domain contains reserved prototype tokens such as [B]proto[/B], constructor, or prototype.
  • Test
  • Run unit and integration tests, and exercise cookie handling code paths.
  • Verify that toxics (or Fuzzing harnesses) do not crash on malformed cookie domains.
  • Deploy
  • Roll out upgrades in stages with monitoring and quick rollback capability.
  • Patch CI pipelines and container images to ensure builds use the fixed package.
  • Monitor
  • Look for anomalous cookie headers in logs: search for strings like Domain=[B]proto[/B] or suspicious domain values.
  • Enable dependency scanning alerts and add tough-cookie to your internal SBOM watchlist.
Apply upgrades promptly: maintainers published v4.1.3 to remediate precisely this weakness, and automated dependency scanners (Dependabot/OSS trackers) will flag older versions.

Detection guidance and forensic indicators​

A small set of easily automatable detections will catch common exploitation attempts:
  • Log and alert on Set‑Cookie or Cookie header values containing [B]proto[/B], prototype, or constructor in the domain attribute.
  • In services that expose cookie ingestion endpoints, record raw Set‑Cookie strings for a brief window to allow retrospective review.
  • Instrument test harnesses to run the public PoC against a staging CookieJar instance to confirm it is no longer vulnerable.
  • Use dependency scanning tools (OSS scanners, SCA tooling) to flag versions earlier than 4.1.3 and to find transitive inclusions.
Because the PoC is short and deterministic, retrospective log searches for Domain=[B]proto[/B] are high‑value first steps in any incident investigation related to this vulnerability. Several advisories explicitly cite the PoC and recommend these same detection approaches.

Why this matters for software supply chains​

Even small, utility libraries like tough-cookie can be critical choke points in large dependency graphs. Prototype pollution is a classic example of a vulnerability that begins in innocuous code (cookie indexing) but can ripple outward into high‑impact problems when chained with other bugs or when an application’s logic assumes a clean prototype environment.
Organizations should treat low‑level libraries in their SBOMs seriously: automated alerts, quick triage, and an established emergency patch workflow are essential. In many enterprise contexts the cost of a rapid update to a small package is far lower than the potential impact of letting a prototype‑pollution flaw remain unpatched. Advisory feeds and distribution maintainers have already integrated the fix into their supervised release streams.

Developer notes — secure coding and defensive patterns​

  • Never use untrusted strings as keys on plain objects that participate in shared state. Prefer Map or Object.create(null) for ad‑hoc maps intended to carry arbitrary keys.
  • When you must accept client‑supplied domain values, canonicalize and validate them strictly: accept only legitimate hostnames and reject tokens that are not valid domain labels.
  • Hardening primitives:
  • Use Object.create(null) to avoid prototype inheritance for internal maps.
  • Use Map when map semantics are needed and you want to avoid prototype interactions.
  • Where possible, adopt secure defaults: disable permissive options like rejectPublicSuffixes=false unless you have a concrete need.
These are straightforward best practices and align with the fix upstream implemented in tough-cookie’s corrected memory store.

Responsible disclosure, timeline and vendor response​

The issue was reported and tracked upstream in the tough-cookie project, with a clear problem report and a small, targeted fix. The maintainers released version 4.1.3 that eliminates prototype inheritance from internal maps and addresses the root cause. Public vulnerability databases (NVD), security vendors (Snyk), and package advisory databases (GitHub Advisory Database) catalog the CVE and list the patched version. A public PoC has circulated, which is why operators are advised to treat the vulnerability as actionable even if widespread exploitation has not been reported.

Putting the severity in context​

Different security trackers assign different scores: the NVD entry records this as CVSS 3.1 = 6.5 (Medium), while some scanning feeds express higher impact ratings for specific deployment contexts. The right interpretation depends on where and how tough-cookie is used in your environment:
  • For a standalone web server that never processes untrusted Set‑Cookie headers, the practical risk is lower.
  • For a proxy, scraper, or multi‑tenant middleware that processes cookies from arbitrary web sites or from users, the risk is higher because an attacker may be able to inject malicious cookies into the processing path.
As always, severity is contextual; prioritize remediation according to the degree of exposure in your architecture.

Final recommendations — a short checklist for WindowsForum readers​

  • Audit: Search for tough-cookie in your projects’ dependency graphs and lockfiles.
  • Patch: Upgrade direct and transitive dependencies to tough-cookie@4.1.3 or later as soon as possible.
  • Harden: Move CookieJar usage away from rejectPublicSuffixes=false unless necessary; sanitize incoming cookies.
  • Detect: Implement quick log searches for Domain=[B]proto[/B] and similar prototype tokens.
  • Automate: Add this CVE to your automated dependency scanning and alerting rules so future regressions are caught early.
  • Educate: Make prototype pollution a coding review talking point — it’s subtle and easily introduced.
Prototype pollution is a textbook example of how language semantics and small implementation choices can become tangible security issues at scale. The fix in tough-cookie is straightforward and narrow; the urgent work now is operational: find instances of the vulnerable library in your supply chain and upgrade or mitigate them promptly.

CVE‑2023‑26136 is a reminder that even tiny utilities deserve rigorous scrutiny and that supply‑chain hygiene — inventory, scanning, and fast patching — remains one of the most effective defenses against subtle but exploitable software errors.

Source: MSRC Security Update Guide - Microsoft Security Response Center
 

Back
Top