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.


Prevention & Best Practices

1. Never Use Math.random() for Security-Sensitive Values

Math.random() is appropriate for game mechanics, UI animations, and statistical sampling. It is never appropriate for:
- Boundary strings in multipart requests
- Session tokens or nonces
- CSRF tokens
- File upload identifiers
- Any value an attacker must not be able to predict

Always use crypto.randomBytes() or crypto.randomUUID() in Node.js for these purposes.

2. Use overrides (npm) or resolutions (Yarn) for Transitive Vulnerabilities

When a vulnerability exists in a transitive dependency, updating your direct dependency may not be enough. Use the appropriate mechanism for your package manager:

npm (v8.3.0+):

"overrides": {
  "vulnerable-package": ">=safe-version"
}

Yarn (v1):

"resolutions": {
  "vulnerable-package": ">=safe-version"
}

3. Run Dependency Audits Regularly

Integrate vulnerability scanning into your CI/CD pipeline:

# npm built-in audit
npm audit

# Trivy (detects CVE-2025-7783 and similar)
trivy fs --scanners vuln .

# Snyk
snyk test

4. Pin and Review package-lock.json

Always commit your package-lock.json to version control. Review it during code review for unexpected version changes. Automated tools like Dependabot and Renovate can keep it current.

5. Relevant Standards

  • CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)
  • OWASP Cryptographic Failures (formerly A3:2017 Sensitive Data Exposure): Covers the use of weak algorithms and RNGs in security-sensitive contexts
  • NIST SP 800-90A: Recommendation for Random Number Generation Using Deterministic Random Bit Generators

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.


References

Frequently Asked Questions

What is the CVE-2025-7783 vulnerability in form-data?

CVE-2025-7783 is a critical flaw where form-data used a cryptographically weak random function (Math.random()) to generate multipart form boundaries, making them guessable by attackers.

How do you prevent unsafe PRNG usage in Node.js?

Use Node.js's built-in `crypto.randomBytes()` or `crypto.randomUUID()` instead of `Math.random()` for any security-sensitive random value generation, such as boundary strings, tokens, or nonces.

What CWE is the unsafe random function vulnerability?

CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG).

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.

Can static analysis detect unsafe PRNG usage?

Yes. Tools like Trivy, Semgrep, and Snyk can detect known vulnerable package versions and patterns like Math.random() used in security-sensitive contexts.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #118

Related Articles

critical

How Unsafe Random Function Usage Happens 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. Versions 2.3.3 and 4.0.5 were affected, and the fix upgrades the package to 4.0.6 (consolidating previously split nested versions) while eliminating the predictable boundary generation. Attackers who could predict or influence multipart boundaries could craft malicious payloads that escape intended field boundaries.

critical

How Unsafe Random Number Generation in form-data Compromises Multipart Form Security and How to Fix It

CVE-2025-7783 exposes a critical vulnerability in the form-data library where unsafe random number generation was used for generating multipart form boundaries, potentially allowing attackers to predict boundary values and manipulate form data. The fix upgrades form-data to versions 4.0.6, 3.0.4, and 2.5.4, which implement proper cryptographic randomness and update security-critical dependencies like hasown and mime-types.

critical

How Unsafe Random Function Vulnerabilities Happen in Node.js and How to Fix Them

A critical vulnerability (CVE-2025-7783) was discovered in the popular `form-data` npm package where an unsafe random function was used to generate boundary strings for multipart form data. This weakness could allow attackers to predict boundary values and potentially inject malicious content into HTTP requests. The fix upgrades form-data to patched versions (2.5.4, 3.0.4, or 4.0.4) that use cryptographically secure random number generation.

high

How Weak bcrypt Salt Rounds Happen in Node.js and How to Fix It

A critical password hashing weakness was discovered in the authentication controller where bcrypt was configured with only 10 salt rounds instead of the recommended minimum of 12. This configuration made user passwords significantly more vulnerable to brute-force attacks if an attacker gained access to the password hash database. The fix was a simple but impactful one-line change that doubles the computational cost required to crack passwords.

medium

How Insufficient Password Hashing Cost Factor Happens in Node.js and How to Fix It

A bcrypt password hashing implementation in the User.js model was using a cost factor of 10, which falls below OWASP's 2024 recommendation of 12 for applications handling sensitive data. This fix upgrades the salt rounds from 10 to 12, increasing the computational work required to crack passwords by approximately 4x, significantly improving protection against brute-force attacks on this e-commerce platform.

critical

How Missing Rate Limiting happens in Express.js and how to fix it

Two public API endpoints in `server.js` — `/api/health` and `/api/contact` — were exposed without any rate limiting middleware, allowing attackers to exhaust server resources or spam an SMTP server with unlimited requests. The fix adds rate limiting to both endpoints, with stricter controls on the resource-intensive `/api/contact` route that triggers email sending operations. This change closes a directly exploitable denial-of-service vector in a production web service.