Back to Blog
critical SEVERITY6 min read

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.

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

Answer Summary

CVE-2025-7783 is an insecure randomness vulnerability (CWE-338) in the `form-data` npm package, where multipart boundary strings were generated with `Math.random()` instead of a cryptographically secure random number generator. This makes boundaries predictable, enabling attackers to craft payloads that collide with or inject into multipart form bodies. The fix upgrades `form-data` to a patched version that uses Node's `crypto` module for boundary generation, and the dependency pin in `server/package-lock.json` is updated accordingly.

Vulnerability at a Glance

cweCWE-338 (Use of Cryptographically Weak Pseudo-Random Number Generator)
fixUpgrade the `form-data` dependency in `server/package-lock.json` to a patched release that uses Node's `crypto.randomBytes()` for boundary generation
riskPredictable multipart boundaries can be guessed or manipulated, enabling boundary collision, payload injection, or request smuggling in multipart/form-data HTTP requests
languageJavaScript / Node.js
root causeform-data generates its multipart boundary string using `Math.random()`, which is not cryptographically secure and is predictable/reproducible
vulnerabilityInsecure Randomness (Unsafe random function in form-data)

Introduction

The server/package-lock.json file in this repository pins dozens of transitive and direct dependencies, including form-data, which sits quietly in the dependency tree at version ^2.3.3 — visible right next to the handlebars entry that was also patched in this pull request. While form-data looks like a boring utility library (it just builds multipart/form-data request bodies for HTTP clients), a flaw deep inside its boundary-generation logic earned it a critical CVE: CVE-2025-7783.

The problem is deceptively simple: to separate the different fields inside a multipart HTTP body, form-data needs to generate a unique "boundary" string. Older versions of the library generated that boundary using Math.random(). That single design decision is why this vulnerability matters — Math.random() is not cryptographically secure, and any value derived from it can, under the right conditions, be predicted or brute-forced by an attacker who controls or observes enough requests.

If your Node.js server uses form-data (directly, or transitively through libraries like axios, node-fetch, or request) to build outbound HTTP requests containing user-supplied data, this vulnerability is directly relevant to you.

The Vulnerability Explained

At a conceptual level, the vulnerable code inside form-data looked something like this:

// Simplified representation of the vulnerable pattern
function getBoundary() {
  return '--------------------------' + Math.random().toString(16);
}

This boundary string is inserted into the Content-Type header (multipart/form-data; boundary=...) and repeated between each field in the request body to tell the receiving server where one field ends and the next begins.

The issue is that Math.random():

  • Is a non-cryptographic PRNG — it is designed for speed, not unpredictability.
  • Can have its internal state inferred by observing a sequence of outputs, in some JS engine implementations.
  • Produces boundaries that are far more guessable than a value generated from crypto.randomBytes().

Why this is dangerous in server/package-lock.json

Look at the dependency declaration visible in the diff for this PR:

"form-data": "^2.3.3",

This pin sits in the exact same package.json/package-lock.json files that were touched to fix the handlebars CVE. That's important: dependency files are living attack surfaces. Every entry in package-lock.json is a promise about exactly which version of code — and exactly which known CVEs — ship with your server.

Example attack scenario: Imagine your Node.js backend uses form-data to relay a file upload or webhook payload to a downstream API. If an attacker can predict or influence the boundary string used in that outgoing multipart request, they could:

  1. Craft a payload whose content collides with the predictable boundary, effectively injecting additional "fields" into the request body that the receiving server did not expect.
  2. Use this to perform request smuggling — sneaking extra parameters or headers past validation logic that assumes boundaries are unique and unguessable.
  3. In multi-tenant or proxy scenarios, potentially cause cross-request data leakage if boundary predictability interacts poorly with connection reuse or caching layers.

None of this requires exotic tooling — an attacker only needs to observe enough boundary values (e.g., from logs, timing, or repeated requests) to start narrowing down the PRNG's likely output space.

The Fix

The remediation for CVE-2025-7783 is to upgrade form-data to a version where the boundary generator uses Node's crypto module instead of Math.random(). Conceptually, the fixed logic looks like this:

Before (vulnerable):

function getBoundary() {
  return '--------------------------' + Math.random().toString(16);
}

After (fixed):

const { randomBytes } = require('crypto');

function getBoundary() {
  return '--------------------------' + randomBytes(16).toString('hex');
}

This mirrors exactly the kind of change applied in this PR's lockfile diff — a version bump plus a refreshed integrity hash, such as we can see for the sibling fix to handlebars:

-      "handlebars": "^4.7.7",
+      "handlebars": "^4.7.9",
-      "version": "4.7.7",
-      "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
+      "version": "4.7.9",
+      "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==",

The same pattern applies to form-data: the entry in server/package.json ("form-data": "^2.3.3") must be bumped to a version line that resolves to a patched release, and server/package-lock.json needs its resolved URL, version, and integrity hash refreshed to match. Both package.json and package-lock.json must be updated together — updating one without the other leaves npm ci installing the old, vulnerable version regardless of what package.json says.

Why both files matter:
- package.json declares the acceptable version range your team intends to allow.
- package-lock.json pins the exact resolved version and hash that gets installed in CI/CD and production. If this file still points to 2.3.3's tarball and integrity hash, the vulnerable boundary-generation code ships regardless of the semver range in package.json.

Prevention & Best Practices

  • Never use Math.random() for anything security-relevant. Boundaries, tokens, session identifiers, CSRF tokens, and password reset codes must always come from crypto.randomBytes(), crypto.randomInt(), or an equivalent CSPRNG.
  • Run npm audit / npm audit fix regularly as part of CI, so known-vulnerable transitive dependencies like form-data surface before they reach production.
  • Use SCA (software composition analysis) scanning — tools like Trivy, Snyk, or GitHub Dependabot continuously diff your lockfile against known CVE databases, exactly how this issue was flagged.
  • Pin and review lockfiles carefully. A package-lock.json diff that only bumps a patch version can still fix a critical CVE — don't dismiss small-looking diffs during code review.
  • Audit transitive dependencies, not just direct ones. form-data is often pulled in indirectly through HTTP client libraries; it's easy to miss in a manual dependency review.

Key Takeaways

  • form-data pinned at ^2.3.3 in server/package-lock.json relied on Math.random() to generate multipart boundaries — a textbook CWE-338 weakness.
  • Predictable boundaries in multipart requests can enable boundary-collision and request-smuggling style attacks against downstream services.
  • Fixing this requires updating both server/package.json (the version range) and server/package-lock.json (the resolved version + integrity hash) — matching the exact pattern used to remediate the neighboring handlebars CVE in this same PR.
  • Dependency-level vulnerabilities hide in files most developers rarely open by hand — automated SCA scanning is essential to catch them.
  • Any library generating identifiers, tokens, or boundaries for security-sensitive protocols should be audited for use of a cryptographically secure RNG.

How Orbis AppSec Detected This

  • Source: The multipart boundary value generated internally by the form-data library whenever the server builds an outgoing multipart/form-data HTTP request (e.g., file uploads, webhook relays).
  • Sink: The boundary-generation function inside form-data's internals, which is embedded via the dependency pinned in server/package-lock.json ("form-data": "^2.3.3").
  • Missing control: No use of a cryptographically secure random number generator (crypto.randomBytes) — the library relied on Math.random(), a weak PRNG unsuitable for security-sensitive values.
  • CWE: CWE-338 — Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG).
  • Fix: Upgrade the form-data dependency declaration and lockfile entry to a patched version that generates boundaries using Node's crypto module.

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 good reminder that "boring" utility dependencies — like a package that just formats multipart HTTP bodies — can carry critical, exploitable weaknesses when they touch anything resembling randomness or identifiers. The vulnerable Math.random()-based boundary generator in form-data, pinned at ^2.3.3 in server/package-lock.json, is a small piece of code with outsized security implications. The fix is straightforward — bump the dependency, refresh the lockfile hash, and let a proper CSPRNG do the job it was designed for. Treat every dependency version pin in your lockfile as a security decision, not just a build detail.

References

Frequently Asked Questions

What is insecure randomness (CVE-2025-7783) in form-data?

It's a flaw where the `form-data` npm package used `Math.random()` — a non-cryptographic pseudo-random number generator — to build the multipart boundary string that separates form fields in an HTTP request body.

How do you prevent insecure randomness vulnerabilities in Node.js?

Always use Node's built-in `crypto` module (`crypto.randomBytes()`, `crypto.randomInt()`) or a vetted library for anything security-relevant, such as tokens, boundaries, session IDs, or nonces — never `Math.random()`.

What CWE is insecure randomness classified under?

CWE-338, "Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)," and its parent CWE-330, "Use of Insufficiently Random Values."

Is pinning a dependency version enough to prevent this vulnerability?

No — pinning alone doesn't fix it if the pinned version is itself vulnerable. You must upgrade to a patched release and keep the lockfile (`package-lock.json`) in sync.

Can static analysis or SCA tools detect this kind of vulnerability?

Yes. Software composition analysis (SCA) scanners like Trivy, npm audit, and Snyk can flag known-vulnerable dependency versions such as this one, and SAST tools can flag direct use of `Math.random()` in security-sensitive code.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #2741

Related Articles

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.

high

How Man-in-the-Middle via ignored TLS options happens in Node.js undici SOCKS5 proxies and how to fix it

`dsh-coding-subscription-oauth` shipped `undici@7.24.8`, a release affected by CVE-2026-9697: when requests are routed through a SOCKS5 proxy, undici silently drops the caller-supplied TLS `connect` options (`ca`, `rejectUnauthorized`, `checkServerIdentity`, `servername`), so certificate pinning and custom trust stores are never applied. The fix pins `undici` to `7.29.0` across the app, `dsh-coding-oauth-core@0.1.1`, and both the production and development dispatchers, and hardens the Docker `de

critical

How Hardcoded HMAC-SHA256 Keys Compromise API Authentication in HarmonyOS and How to Fix It

A critical vulnerability in the Bika application exposed a hardcoded HMAC-SHA256 signing key directly in the Constants.ets file, allowing attackers to forge valid API requests. The fix implements runtime key deobfuscation using XOR masking, removing the plaintext credential from both source code and compiled binaries. This change demonstrates why symmetric keys must never be embedded in client-side code.

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 the use of an unsafe random number generator to produce multipart form boundaries, making those boundaries predictable by an attacker. The fix upgrades `form-data` to versions 2.5.4, 3.0.4, and 4.0.4, which replace the weak random function with a cryptographically secure alternative. This change was applied to the `example-apps/collector/package-lock.json` and `package.json` files in the Instana collector example

critical

How Plaintext Token Storage happens in TypeScript/Tauri and how to fix it

A critical vulnerability in a Tauri desktop application allowed GitHub API tokens with full `repo` scope to be written to plaintext local storage files via the `getAllSettings()` function in `src/config/settings.ts`. Any process with filesystem access — including malware, other apps, or a logged-in attacker — could silently extract these tokens. The fix introduces a `SENSITIVE_KEYS` exclusion set that prevents credentials from being serialized to disk.

critical

How a vulnerable websocket-driver dependency happens in Node.js lockfiles and how to fix it

A Trivy scan flagged `websocket-driver@0.7.4` in this repository's `bun.lock` as affected by CVE-2026-54466, a critical issue in a WebSocket protocol handler that parses untrusted HTTP upgrade requests and frame data. The fix upgrades the package to `0.7.5` and adds an explicit `websocket-driver` entry to the lockfile's override block so every transitive consumer — webpack-dev-server, sockjs, faye-websocket — resolves to the patched build instead of the pinned vulnerable one.