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
•Technically reviewed by Anupam Mediratta•Published August 31, 2026•Reviewed 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.

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.

Prevention and further reading

Frequently Asked Questions

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

file_md5() MD5 Digest Lets Agent Docs Collide (CWE-328)

A first-party maintenance script used `hashlib.md5()` inside a `file_md5(path)` helper to decide whether the per-vendor agent instruction documents in a repository were byte-equivalent. Because MD5 is collision-broken, two meaningfully different documents could be crafted to produce the same digest and pass the "these files are in sync" gate. The fix replaces the MD5 call with `hashlib.sha256()` over the same `path.read_bytes()` input.

high

SQLite Auth Database Plaintext Storage in InitAuthDB()

The `InitAuthDB()` function opened SQLite databases without restricting filesystem permissions, leaving bcrypt password hashes, session tokens, and user settings exposed to any local account with read access. The fix explicitly sets `os.Chmod(filepath, 0600)` immediately after database creation, ensuring only the owner can access authentication secrets.

critical

Hybridauth Telegram OAuth Timing Attack in `authenticateCheckError()`

The Telegram OAuth provider in Hybridauth relied on `strcmp()` to validate HMAC-SHA256 signatures, exposing a timing side-channel that could allow attackers to forge authentication tokens character by character. The fix replaces this with PHP's timing-safe `hash_equals()` function, eliminating the information leak.

high

undici 7.29.0 TLS Bypass: BalancedPool Drops Connect Options

A critical flaw in undici's BalancedPool implementation silently discards TLS certificate validation settings when routing requests through certain connection paths. Attackers on the network could intercept HTTPS traffic without triggering validation errors. The fix preserves connect options throughout the connection lifecycle.

critical

Model Fetching Without Integrity Verification in ONNX Loading

A machine learning application was fetching ONNX model files and chunks over the network without verifying their integrity, creating an opening for model poisoning attacks. The fix adds cryptographic integrity verification at the point where downloaded chunks are reassembled and cached, ensuring models have not been modified in transit or at rest.

high

nanoid 3.3.11 Integer Overflow: Predictable ID Generation

An integer overflow in nanoid 3.3.11's internal randomness generation causes the library to fall back to predictable ID sequences, undermining the cryptographic guarantees of its supposedly unguessable identifiers. The fix upgrades the dependency tree to patched versions 3.3.12 or 5.1.11.