Back to Blog
critical SEVERITY9 min read

How Unsafe Random Functions Happen in Node.js form-data and How to Fix It

CVE-2025-7783 is a critical vulnerability in the `form-data` npm package caused by its use of an unsafe random function to generate multipart form boundaries. This flaw allows attackers to predict boundary values, potentially enabling them to manipulate or inject content into multipart requests. The fix upgrades `form-data` to version 4.0.6 and enforces this version across the entire dependency tree using a `package.json` `overrides` directive.

O
By Orbis AppSec
Published August 26, 2026Reviewed August 26, 2026

Answer Summary

CVE-2025-7783 is a critical vulnerability (CWE-338) in the `form-data` npm package where a cryptographically weak pseudo-random number generator (PRNG) is used to generate multipart form boundaries, making them predictable to attackers. This affects Node.js applications that construct multipart/form-data requests. The fix is to upgrade `form-data` to version 2.5.4, 3.0.4, or 4.0.4+ and use a `package.json` `overrides` field to force all transitive dependencies to use the patched version, preventing older vulnerable versions from being pulled in through indirect dependencies like `request`.

Vulnerability at a Glance

cweCWE-338
fixUpgrade form-data to 4.0.6 and enforce the version across all transitive dependencies via package.json overrides
riskAttackers can predict multipart boundaries, enabling content injection or request manipulation
languageJavaScript / Node.js
root causeform-data used Math.random() instead of a cryptographically secure random source for boundary string generation
vulnerabilityUnsafe PRNG for multipart boundary generation

How Unsafe Random Functions Happen in Node.js form-data and How to Fix It

Vulnerability at a Glance
| Field | Detail |
|---|---|
| Vulnerability | Unsafe PRNG for multipart boundary generation |
| CWE | CWE-338 |
| Language | JavaScript / Node.js |
| Risk | Attackers can predict multipart boundaries, enabling content injection or request manipulation |
| Root Cause | form-data used Math.random() instead of a cryptographically secure random source |
| Fix | Upgrade form-data to 4.0.6 and enforce via package.json overrides |


Direct Answer

CVE-2025-7783 is a critical vulnerability (CWE-338) in the form-data npm package where a cryptographically weak pseudo-random number generator (PRNG) — specifically Math.random() — was used to generate multipart form boundaries, making them predictable to attackers. This affects Node.js applications that construct multipart/form-data requests. The fix is to upgrade form-data to version 4.0.6 and use a package.json overrides field to force all transitive dependencies to use the patched version, preventing older vulnerable versions from being pulled in through indirect dependencies like request.


Introduction

The package-lock.json file in this Node.js project pinned form-data at version 2.3.3 — a version that contains a subtle but critical flaw: it generates multipart form boundaries using Math.random(), JavaScript's built-in pseudo-random number generator, which is not cryptographically secure. This flaw is tracked as CVE-2025-7783 and carries a critical severity rating.

Multipart boundaries are the delimiter strings that separate fields and file attachments in multipart/form-data HTTP requests. They look like this in a raw HTTP request:

Content-Type: multipart/form-data; boundary=----FormBoundary7MA4YWxkTrZu0gW

------FormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="upload.png"
...

If an attacker can predict the boundary string — which becomes trivially possible when Math.random() is the source of entropy — they can craft requests that manipulate how the server parses multipart payloads. For developers building APIs, file upload services, or any system that constructs outbound multipart requests programmatically, this vulnerability is a silent threat hiding in a widely-trusted package.


The Vulnerability Explained

What's Wrong with Math.random()?

Math.random() in JavaScript is designed for statistical randomness, not security. Its output is seeded by the JavaScript engine and is predictable given enough observed outputs or knowledge of the seed state. The V8 engine (used by Node.js) implements Math.random() using the xorshift128+ algorithm — fast, but entirely unsuitable for generating security-sensitive values.

In form-data versions prior to the patch, the boundary string for multipart requests was generated using a function roughly equivalent to:

// Vulnerable pattern in form-data < 2.5.4 / 3.0.4 / 4.0.4
function generateBoundary() {
  var boundary = '--------------------------';
  for (var i = 0; i < 24; i++) {
    boundary += Math.floor(Math.random() * 10).toString(10);
  }
  return boundary;
}

The critical problem here: Math.random() produces a deterministic sequence once the seed is known. In server-side environments where an attacker can make many requests and observe timing or other side-channel information, the seed can be inferred — and future (or past) boundary values can be predicted.

How Could This Be Exploited?

Consider a Node.js service that:
1. Accepts a user-initiated action (e.g., uploading a profile picture)
2. Internally forwards that file to a backend API using form-data to construct a multipart/form-data request

An attacker who can:
- Observe multiple outbound boundary strings (e.g., through error messages, logs, or timing analysis)
- Or influence when the form-data boundary is generated relative to other Math.random() calls in the application

...could predict the next boundary string. With a known boundary, they could craft a malicious payload that:

  • Injects additional form fields into the multipart body by embedding the boundary string inside a field value, causing the server to misparse the request
  • Truncates or replaces file content by injecting a premature boundary, effectively splitting or corrupting the upload
  • Bypasses server-side validation that relies on field ordering or field counts in the multipart body

This is a content injection attack against multipart parsing — a class of vulnerability that has historically been used to bypass file type checks, inject malicious filenames, or smuggle unauthorized data into backend systems.

Real-World Impact for This Application

In the affected project, form-data at version 2.3.3 was present both as a direct dependency resolution and as a transitive dependency pulled in by the request package (which pinned form-data: ~2.3.2). This means even if the top-level dependency was updated, the vulnerable version could still be instantiated through request's dependency subtree — a subtle but important attack surface that the fix specifically addresses.


The Fix

The remediation involved two coordinated changes: updating package-lock.json to resolve to the patched version, and adding a package.json overrides directive to enforce the patched version across the entire dependency tree, including transitive consumers like request.

Change 1: package-lock.json — Resolving to the Patched Version

The lock file was updated to point form-data to version 4.0.6 instead of the vulnerable 2.3.3. Additionally, the request package's pinned dependency on form-data: ~2.3.2 was overridden:

-        "form-data": "~2.3.2",
+        "form-data": "4.0.6",

This ensures that when request is installed, it no longer pulls in the vulnerable 2.3.x range.

Change 2: package.json — Enforcing the Override Globally

The most important change for long-term security is the addition of the overrides field in package.json:

+  "overrides": {
+    "form-data": "4.0.6"
+  }

Before:

{
  "dependencies": {
    "perlin-noise": "^0.0.1",
    "sharp": "^0.33.1"
  }
}

After:

{
  "dependencies": {
    "perlin-noise": "^0.0.1",
    "sharp": "^0.33.1"
  },
  "overrides": {
    "form-data": "4.0.6"
  }
}

The overrides field (introduced in npm 8.3.0) instructs npm to forcibly resolve all instances of form-data — regardless of which package requested it and what version range they specified — to 4.0.6. This is the critical defense against transitive dependency vulnerabilities, where a direct dependency (like request) drags in an outdated, vulnerable version of a sub-dependency.

Why This Two-File Fix Is Necessary

Without the overrides in package.json, running npm install in the future could silently re-introduce the vulnerable 2.3.3 version through request's ~2.3.2 version range. The lock file change alone is fragile — it can be overwritten. The overrides directive makes the security constraint durable and declarative, surviving future npm install runs.

In form-data 4.0.4+, the boundary generation was patched to use crypto.randomBytes() — Node.js's cryptographically secure random byte generator — instead of Math.random(). The patched implementation looks like:

// Secure pattern in form-data >= 4.0.4
const crypto = require('crypto');

function generateBoundary() {
  return crypto.randomBytes(20).toString('hex');
}

crypto.randomBytes() draws entropy from the operating system's secure random source (e.g., /dev/urandom on Linux, CryptGenRandom on Windows), making the output computationally infeasible to predict.


Key Takeaways

  • Math.random() in form-data < 2.5.4/3.0.4/4.0.4 generates predictable multipart boundaries — an attacker who can observe or infer boundary values can inject content into multipart requests
  • Updating your direct dependency isn't always enough — the request package's form-data: ~2.3.2 pin meant the vulnerable version could still be installed transitively without the overrides fix
  • The package.json overrides field is a durable security control — it survives future npm install runs and prevents accidental re-introduction of the vulnerable version
  • crypto.randomBytes() is the correct replacement for Math.random() in boundary generation — it uses OS-level entropy and is computationally infeasible to predict
  • Trivy's static analysis caught this before it was confirmed reachable — scanning your package-lock.json for known CVEs is a low-cost, high-value security practice

How Orbis AppSec Detected This

  • Source: The form-data package version 2.3.3 resolved in package-lock.json, which is pulled in both directly and transitively through the request package's form-data: ~2.3.2 dependency range
  • Sink: The boundary generation function inside form-data's multipart stream constructor, which called Math.random() to produce the delimiter string embedded in Content-Type: multipart/form-data; boundary=... headers
  • Missing control: No cryptographically secure entropy source was used; Math.random() provided no unpredictability guarantees, and no version constraint prevented transitive consumers from loading the vulnerable version
  • CWE: CWE-338 — Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)
  • Fix: Upgraded form-data to 4.0.6 (which uses crypto.randomBytes() for boundary generation) and added an overrides directive in package.json to enforce this version across all transitive dependencies

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

CVE-2025-7783 is a reminder that security vulnerabilities can hide in the most mundane-seeming code paths — boundary string generation isn't where most developers think to look for critical flaws. But the choice between Math.random() and crypto.randomBytes() is the difference between a predictable, exploitable value and one that's computationally secure.

What makes this vulnerability particularly tricky is the transitive dependency problem: even if you knew to update form-data, the request package's pinned ~2.3.2 range would silently pull the vulnerable version back in. The correct fix required both updating the resolved version in package-lock.json and adding an overrides directive in package.json to make the constraint durable.

For Node.js developers: audit your package-lock.json for known CVEs, use overrides aggressively when transitive vulnerabilities are involved, and treat Math.random() as off-limits for any value that must be unpredictable to an adversary.


Prevention and further reading

Frequently Asked Questions

Is upgrading the direct dependency enough to fix CVE-2025-7783?

Not always. Transitive dependencies like `request` may still pull in the vulnerable version. You must also use `overrides` (npm) or `resolutions` (Yarn) in package.json to force the patched version across the entire dependency tree.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #118

Related Articles

critical

How insufficient PBKDF2 iterations happen in JavaScript and how to fix it

A critical vulnerability in `libs/wgs/pbkdf2.js` used only 1 iteration for PBKDF2 password hashing, making passwords trivially crackable. The fix increases iterations to 600,000, aligning with OWASP 2023 recommendations and preventing GPU-accelerated brute-force attacks.

critical

How Hardcoded Encryption Salts Compromise Credential Storage in Node.js and How to Fix It

A critical vulnerability in `scripts/bench-cpu.js` used a hardcoded static salt (`'byok-relay-salt'`) when deriving encryption keys with scrypt, allowing attackers to decrypt all encrypted credentials if the encryption secret was compromised. The fix replaces the hardcoded salt with cryptographically secure random bytes generated per operation, ensuring each user's encrypted credentials require a unique derived key.

high

How weak scrypt password hashing happens in Node.js and how to fix it

The `hashPass` function in `store-saas/server.mjs` used Node.js's `crypto.scryptSync` with default cost parameters (N=16384, r=8, p=1), making stored password hashes cheap to attack with modern GPUs. The fix increases the CPU/memory cost factor to N=131072 and parallelization to p=2, dramatically raising the computational effort required to brute-force stolen hashes.

high

How Dependency Version Pinning Prevents Supply Chain Attacks in Node.js and How to Fix It

A critical supply chain vulnerability in `package.json` allowed automatic updates to a cryptographic library with known weaknesses. By pinning `rijndael-js` to version `2.0.0` instead of allowing `^2.0.0` updates, the fix prevents silent installation of vulnerable versions that could expose downstream consumers to weak block cipher modes and authentication bypasses.

critical

How Insecure Randomness in form-data happens in Node.js and how to fix it

The `form-data` npm package, pinned at `^2.3.3` in `server/package-lock.json`, generated multipart form boundaries using the insecure `Math.random()` function instead of a cryptographically secure random source. This predictable boundary generation (CVE-2025-7783) could allow an attacker to guess or influence multipart boundaries, opening the door to request smuggling and payload injection in HTTP requests built by the server.

high

How Interpretation Conflict Vulnerability happens in Node.js and how to fix it

node-forge versions up to 1.3.1 shipped an ASN.1 parser vulnerable to an interpretation conflict that could let attackers bypass cryptographic signature verification, alongside a related unbounded recursion flaw (CVE-2025-66031) that enables denial-of-service. Upgrading the dependency to node-forge 1.4.0 patches both issues by hardening the ASN.1 decoder against malformed and adversarially crafted input.