HighGHSA-rhr8-3pm8-cq29CVE-2026-119191

Forge: ASN.1 resource exhaustion via BIT STRING decoding in asn1.fromDer

Published 10 Nov 2026 · Updated 10 Nov 2026

Is your project on an affected node-forge?

Private repo? Upload its lockfile instead, or connect GitHub to check every repo.

Affected package

Affected
<1.4.1
Fixed in
1.4.1
Update to the fixed version for your release line, for example:npm install node-forge@1.4.1

Details

Summary

asn1.fromDer() decodes the contents of BIT STRINGs as ASN.1 by default. That decode ignored the maxDepth recursion limit (an incomplete fix of CVE-2025-66031) and also decoded nested BIT STRINGs, keeping a full copy of the decoded contents at each level, so small crafted DER or PEM inputs (certificates, keys, PKCS structures) use excessive CPU time and memory and can abort the Node.js process. Fixed in node-forge 1.4.1.

Details

asn1.fromDer() attempts by default to decode the contents of primitive BIT STRINGs as ASN.1 (decodeBitStrings, default true). Two flaws in that decode in lib/asn1.js allow resource exhaustion:

1. _fromDer() started the BIT STRING decode with a new options object, {strict: true, decodeBitStrings: true}, that did not include maxDepth. Inside that decode the depth check compared against undefined and never triggered, so the recursion limit added for CVE-2025-66031 (GHSA-554w-wpv2-vw27) was not enforced for anything below a decoded BIT STRING. Both the global asn1.maxDepth (default 256) and the per-call maxDepth option were ignored there. Errors in this speculative decode, including stack overflows, are caught and the contents kept as raw bytes, so deep nesting is parsed instead of rejected. This is an incomplete fix of CVE-2025-66031. 2. BIT STRINGs nested inside decoded BIT STRING contents were decoded as well, and asn1.create() gives each decoded BIT STRING a deep copy of its decoded contents (obj.original, used by toDer() to detect changes). Memory use therefore grows quadratically with the nesting depth, even within the default depth limit.

No specification is violated; the parser lacks a resource limit.

The fix passes maxDepth to the BIT STRING decode, and decodes BIT STRING contents with decodeBitStrings: false, so only one level of BIT STRING contents is decoded. Contents that would exceed the limit, and BIT STRINGs inside decoded BIT STRING contents, are kept as undecoded BIT STRING values; toDer() writes their original bytes. Common structures such as SubjectPublicKeyInfo only use one level. Code that relied on nested decoding can decode the inner value explicitly with asn1.fromDer().

Patches

Fixed in node-forge 1.4.1. The maxDepth limit now applies to ASN.1 decoded from BIT STRING contents, and only one level of BIT STRING contents is decoded.

Workarounds

Code that calls forge.asn1.fromDer() directly and does not need encapsulated BIT STRING contents decoded can pass {decodeBitStrings: false}. This changes the parsed structure. The higher-level PKI, PKCS and TLS APIs do not expose this option and rely on the decoded form. For those, the only remedy is upgrading.

References

  • CVE-2025-66031 / https://github.com/digitalbazaar/forge/security/advisories/GHSA-554w-wpv2-vw27 (ASN.1 Unbounded Recursion, fixed in 1.3.2)
  • https://cwe.mitre.org/data/definitions/674.html (CWE-674: Uncontrolled Recursion)
  • https://cwe.mitre.org/data/definitions/400.html (CWE-400: Uncontrolled Resource Consumption)

Severity

CVSS 3.1: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H (7.5, High) CVSS 4.0 (informational): CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N (8.7) Both versions rate this High. The severity rating follows CVSS 3.1.

Credits

Reported by Ethan Kim (cremit.io), Youngjoon Kim (@acorn421) and Brian Lee (@hibrian827) of Georgia Tech SSLab, @arpitjain099, and Mike Read.

Proof of concept

Input shape: n nested primitive BIT STRINGs (03 <len> 00 <inner>) around a NULL. For example:

var asn1 = forge.asn1; var obj = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, ''); for(var i = 0; i < 2000; ++i) { obj = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false, [obj]); } var der = asn1.toDer(obj).getBytes(); // 9,886 bytes asn1.fromDer(der, {maxDepth: 10});

On 1.4.0 (Node.js 24) this parses all 2,000 levels despite maxDepth: 10, taking about 0.5-1 s and 600 MB. One SEQUENCE holding 20 such chains (about 198 KB) aborts a process with the default heap with "FATAL ERROR: JavaScript heap out of memory", which the application cannot catch. Within the depth limit, 80 chains of 254 levels (92 KB) still use about 0.8 GB. The same inputs wrapped as PEM reach pki.publicKeyFromPem() and pki.certificateFromPem() (a 20 KB certificate PEM used 1.3 GB before being rejected). On 1.4.1 these inputs parse in a few milliseconds and about 55-65 MB.

Regression tests in tests/unit/asn1.js: "should limit depth in decoded BIT STRINGs (default)", "should limit depth in decoded BIT STRINGs (maxDepth)", "should only decode one level of BIT STRINGs" and "should not decode BIT STRINGs in many BIT STRINGs".

Impact

A remote, unauthenticated attacker can supply a small DER or PEM structure with nested BIT STRINGs to any API that parses with asn1.fromDer() using default options. This includes pki.certificateFromPem(), pki.publicKeyFromPem(), the PKCS#7, PKCS#8 and PKCS#12 loaders, and server certificates received by forge's TLS client. Parsing such input consumes excessive CPU time and memory before the structure is rejected. On Node.js 24 a 10 KB input used about 600 MB and a 25 KB input about 1.4 GB. Inputs of about 25 KB abort a process limited to a 512 MB heap, and about 200 KB abort a process with the default heap, with a fatal out-of-memory error that the application cannot catch. Inputs that stay within the depth limit still reach about 0.8 GB at 92 KB and 1.8 GB at 231 KB. The result is denial of service. There is no confidentiality or integrity impact.

Is GHSA-rhr8-3pm8-cq29 in any of your client repos?

Checking one package by hand is fine. Checking it across forty client projects is not. NPMscan reads the lockfiles in every GitHub repo you pick, finds each project with an affected node-forge, and gives you the command that fixes it.

More advisories for node-forge

  • LowCVE-2026-125335Forge: Ed25519 signature verification accepts small-order public keys · 10 Nov 2026
  • MediumCVE-2026-125332Forge: TLS client crash on an unsupported ServerHello compression method · 10 Nov 2026
  • MediumCVE-2026-125333Forge: ReDoS in forge.http response header and cookie trimming · 10 Nov 2026
  • MediumCVE-2026-125334Forge: TLS client crash on a handshake record received without a session · 10 Nov 2026
  • LowCVE-2026-118774Forge: Certificate signature verified with the issuer's RSA signature scheme · 10 Nov 2026
  • LowCVE-2026-118776Forge: Incomplete PKCS#7 padding validation in AES/DES CBC and ECB decryption · 10 Nov 2026
All 36 advisories for node-forge

References

Advisory data from OSV.dev (opens OSV.dev in a new tab), refreshed daily.