Back to Blog
critical SEVERITY7 min read

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.

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

Answer Summary

CVE-2025-7783 is a critical vulnerability (CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator) in the `form-data` npm package, affecting versions prior to 2.5.4, 3.0.4, and 4.0.4. The flaw lies in the use of a non-cryptographic `Math.random()` function to generate multipart form boundaries, making them predictable. The fix upgrades `form-data` to 4.0.6 in the `package-lock.json`, consolidating the previously split nested dependency under `node_modules/axios/node_modules/form-data` into a single resolved version with a cryptographically secure random boundary generator.

Vulnerability at a Glance

cweCWE-338
fixUpgrade form-data to 4.0.6, which replaces Math.random() with a cryptographically secure random source
riskPredictable multipart boundaries enable boundary injection and payload smuggling
languageJavaScript / Node.js
root causeform-data used Math.random() instead of a CSPRNG to generate multipart form boundaries
vulnerabilityUnsafe Pseudo-Random Number Generator for Multipart Boundary

How Unsafe Random Function Usage Happens in Node.js form-data and How to Fix It

In the ocapi-proxy project, a critical vulnerability was discovered in a transitive dependency: the form-data npm package. Trivy flagged CVE-2025-7783 in package-lock.json, revealing that both the direct node_modules/form-data (version 2.3.3) and a nested copy under node_modules/axios/node_modules/form-data (version 4.0.5) were affected by an unsafe random number generator used to construct multipart form boundaries.

This might sound like a low-level implementation detail, but the consequences are significant: predictable multipart boundaries can be exploited to smuggle data across field boundaries, potentially bypassing server-side validation logic or injecting unexpected content into form fields.


The Vulnerability Explained

What Are Multipart Form Boundaries?

When form-data constructs a multipart/form-data HTTP request body, it generates a unique boundary string that separates individual form fields. This boundary looks something like:

---------------------------123456789abcdef
Content-Disposition: form-data; name="file"; filename="upload.txt"

<file content here>
---------------------------123456789abcdef--

The boundary must be unique and unpredictable — it must not appear anywhere within the actual field content. If it does, a parser will prematurely terminate the field and treat the rest as a new part.

The Vulnerable Code Pattern

Before the fix, form-data used JavaScript's Math.random() to generate these boundary strings. Math.random() is a pseudo-random number generator (PRNG) — it is explicitly not designed for security-sensitive use. Its output is:

  • Seeded deterministically in many V8 engine versions
  • Statistically predictable if an attacker can observe enough outputs
  • Not suitable for generating secrets, tokens, or structural delimiters in security contexts

The vulnerable package version in package-lock.json was pinned to:

"node_modules/form-data": {
  "version": "2.3.3",
  "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
  "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
  ...
}

And a second copy was nested under axios:

"node_modules/axios/node_modules/form-data": {
  "version": "4.0.5",
  "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
  "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
  ...
}

Both versions relied on Math.random() for boundary generation — meaning two separate vulnerable copies existed in the dependency tree simultaneously.

How an Attacker Could Exploit This

Consider a scenario where ocapi-proxy forwards multipart form uploads to an upstream API. An attacker who:

  1. Observes multiple requests from the application (or triggers them via a public endpoint), and
  2. Collects enough Math.random() outputs embedded in boundary strings

...could reconstruct the PRNG state and predict future boundary values. With a predicted boundary in hand, the attacker crafts a file upload where the file content itself contains the predicted boundary string:

---------------------------<predicted-boundary>
Content-Disposition: form-data; name="description"

safe text
---------------------------<predicted-boundary>
Content-Disposition: form-data; name="file"; filename="evil.txt"

<injected content that the upstream API treats as a new field>
---------------------------<predicted-boundary>--

The upstream parser sees this as a legitimately structured multipart body with an extra field — one the application never intended to send. Depending on the upstream API's behavior, this could:

  • Override server-side parameters with attacker-controlled values
  • Bypass field-level validation (e.g., inject a role=admin field)
  • Cause unexpected application logic to execute

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


The Fix

What Changed in the Diff

The pull request made two key structural changes to package-lock.json:

1. Removed the nested axios copy of form-data (4.0.5)

The entire node_modules/axios/node_modules/form-data block was deleted:

-    "node_modules/axios/node_modules/form-data": {
-      "version": "4.0.5",
-      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
-      "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
-      "dependencies": {
-        "asynckit": "^0.4.0",
-        "combined-stream": "^1.0.8",
-        "es-set-tostringtag": "^2.1.0",
-        "hasown": "^2.0.2",
-        "mime-types": "^2.1.12"
-      }
-    },

This eliminates the second vulnerable copy. Axios will now resolve to the single top-level form-data entry.

2. Upgraded the top-level form-data from 2.3.3 to 4.0.6

 "node_modules/form-data": {
-  "version": "2.3.3",
-  "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
-  "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+  "version": "4.0.6",
+  "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
+  "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
   "dependencies": {
     "asynckit": "^0.4.0",
-    "combined-stream": "^1.0.6",
-    "mime-types": "^2.1.12"
+    "combined-stream": "^1.0.8",
+    "mime-types": "^2.1.12",
+    "es-set-tostringtag": "^2.1.0"
   }
 }

Version 4.0.6 (patching the 4.0.4 security fix) replaces Math.random() with Node.js's crypto.randomBytes() for boundary generation, producing cryptographically unpredictable boundaries that cannot be reconstructed by an observer.

Why Both Changes Were Necessary

Simply upgrading the top-level entry to 4.0.6 would not have been sufficient — npm's hoisting behavior meant that axios was pinned to its own nested copy at 4.0.5. By removing that nested entry, the fix ensures a single, patched version is used across the entire dependency tree. This is a common but often overlooked attack surface: a package can appear "fixed" at the top level while a vulnerable nested copy remains active.


Prevention & Best Practices

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

In Node.js, Math.random() is explicitly documented as not cryptographically secure. For any value that needs to be unguessable — boundaries, tokens, nonces, session IDs — use:

const crypto = require('crypto');
const boundary = crypto.randomBytes(16).toString('hex');

2. Audit Your Full Dependency Tree, Not Just Direct Dependencies

CVE-2025-7783 existed in two places in this project's lock file — one direct and one transitive. Use tools that inspect the full tree:

npm audit
trivy fs --scanners vuln .

3. Lock File Hygiene: Watch for Nested Duplicates

When a nested copy of a package exists (e.g., node_modules/axios/node_modules/form-data), it can silently shadow a patched top-level version. After any security upgrade, verify no nested copies remain:

find node_modules -name "package.json" -path "*/form-data/package.json" | xargs grep '"version"'

4. Pin to Patch Releases in package.json

Using ^4.0.4 in package.json allows npm to resolve to patched versions automatically. Avoid overly loose ranges like * or >=2 for security-sensitive packages.

5. Enable Automated Dependency Scanning in CI

Integrate Trivy, Snyk, or npm audit into your CI pipeline so vulnerable dependency versions are caught before they reach production:

- name: Security audit
  run: trivy fs --exit-code 1 --severity CRITICAL,HIGH .

Security Standards Reference

  • CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)
  • OWASP A02:2021 – Cryptographic Failures: covers the use of weak or inappropriate cryptographic algorithms
  • OWASP Dependency-Check: recommends automated scanning of all transitive dependencies

Key Takeaways

  • Math.random() in form-data 2.3.3 and 4.0.5 generated predictable multipart boundaries — a structural delimiter that must be unguessable to prevent boundary injection attacks.
  • Two vulnerable copies existed simultaneously in package-lock.json: one at node_modules/form-data (2.3.3) and one nested under node_modules/axios/node_modules/form-data (4.0.5) — both had to be addressed.
  • Removing the nested axios copy was as important as upgrading the top-level entry; leaving it would have kept the vulnerability active for all axios-initiated requests.
  • The fix consolidates to a single form-data 4.0.6 entry, which uses crypto.randomBytes() internally — making boundaries cryptographically random and unpredictable.
  • Trivy's static analysis caught this in package-lock.json before it could be exploited, demonstrating the value of scanning lock files (not just package.json) in your CI pipeline.

How Orbis AppSec Detected This

  • Source: The package-lock.json file resolved form-data to version 2.3.3 (direct) and 4.0.5 (nested under axios), both of which use Math.random() to generate multipart form boundaries — a value derived from a seeded, non-cryptographic PRNG.
  • Sink: The boundary string generated by Math.random() is embedded directly into the Content-Type: multipart/form-data; boundary=<value> header and into the serialized request body. Any code path in ocapi-proxy that constructs a multipart request (directly or via axios) passes through this vulnerable code.
  • Missing control: No cryptographically secure random source (crypto.randomBytes()) was used. The boundary value was not validated or re-generated with a CSPRNG before being written into the HTTP body.
  • CWE: CWE-338 — Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)
  • Fix: Both the top-level form-data entry and the nested axios-scoped copy were upgraded to 4.0.6, which replaces Math.random() with crypto.randomBytes() for boundary generation.

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 don't always look like classic injection flaws or authentication bypasses. Sometimes, a single function choice — Math.random() instead of crypto.randomBytes() — buried deep in a transitive dependency can open the door to boundary injection and payload smuggling attacks.

What made this case particularly tricky was the dual presence of the vulnerable package: both a direct dependency at version 2.3.3 and a nested copy under axios at 4.0.5 needed to be addressed. A partial fix — upgrading only one — would have left the vulnerability active.

The broader lesson: treat your package-lock.json as a first-class security artifact. Scan it, audit it, and verify that security upgrades propagate through the entire resolved dependency tree — not just the top-level entries.


References

Frequently Asked Questions

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

CVE-2025-7783 is a critical vulnerability where the form-data npm package used Math.random() — a non-cryptographic pseudo-random number generator — to create multipart form boundaries, making them predictable and exploitable.

How do you prevent unsafe random usage in Node.js multipart libraries?

Use a cryptographically secure random number generator such as Node.js's built-in crypto.randomBytes() for any security-sensitive value like multipart boundaries, tokens, or nonces. Upgrade form-data to 4.0.4 or later.

What CWE is this unsafe random vulnerability?

This vulnerability maps to CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG), because Math.random() is not suitable for security-sensitive randomness.

Is upgrading form-data enough to prevent this vulnerability?

Yes, upgrading to form-data 2.5.4, 3.0.4, or 4.0.4+ patches the PRNG issue. However, also ensure no nested or transitive copies of older form-data versions remain in your lock file.

Can static analysis detect unsafe random function usage in Node.js?

Yes. Tools like Trivy (which flagged this CVE) and Semgrep can identify use of Math.random() in security-sensitive contexts and flag outdated dependency versions with known CVEs.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #18

Related Articles

critical

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.

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.