Skip to content

GHSA-5x38-2vff-x55p CVE-2026-119190

Forge: X.509 basicConstraints bypass in verifyCertificateChain() via deprecated extension OIDs 2.5.29.10 and 2.5.29.13

High npm
Severity
7.4 / 10
High · CVSS 3.1
Exploit chance (EPSS)
0.25%
Chance of exploitation in the next 30 days. Higher than 15.89% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-295 Improper Certificate Validation
CWE-436 Interpretation Conflict
Fix
1.4.1
Upgrade node-forge to this version or later

Description

Summary

node-forge parses certificate extensions with the deprecated OIDs 2.5.29.10 and 2.5.29.13 as basicConstraints, and forge.pki.verifyCertificateChain() used the first extension with that name. A certificate with a forged deprecated-OID extension (cA TRUE) before its genuine basicConstraints extension (cA FALSE) is accepted as a CA certificate, and the same technique can hide a pathLenConstraint. Fixed in node-forge 1.4.1.

Details

lib/oids.js maps the deprecated certificate extension OIDs 2.5.29.10 and 2.5.29.13 to the name basicConstraints, the same name as the RFC 5280 extension 2.5.29.19. certificateExtensionFromAsn1() in lib/x509.js decodes extensions by name, so these extensions are decoded into the same cA and pathLenConstraint fields. cert.getExtension() returns the first extension with a matching name, and pki.verifyCertificateChain() looked up basicConstraints by name.

As a result, a certificate that contains a 2.5.29.10 (or 2.5.29.13) extension with cA TRUE before its genuine basicConstraints extension with cA FALSE is accepted as a CA certificate. The same technique can hide a CA certificate's pathLenConstraint. A deprecated-OID extension on its own also satisfies the check added for GHSA-2328-f5f3-gj25 (CVE-2026-33896) that basicConstraints is present. RFC 5280 section 4.2.1.9 defines basicConstraints only as id-ce 19 (2.5.29.19), and the path validation checks in section 6.1.4 apply to that extension.

The fix makes chain verification look up basicConstraints by OID (cert.getExtension({id: '2.5.29.19'})). This covers both the cA and the pathLenConstraint checks. Certificate parsing, the OID table and cert.getExtension('basicConstraints') are unchanged.

Patches

Fixed in node-forge 1.4.1. Chain verification now uses only the basicConstraints extension with OID 2.5.29.19.

Workarounds

None in node-forge itself. Applications that pass an options.verify callback to pki.verifyCertificateChain() (or verify to forge.tls.createConnection()) can reject chains in which a non-leaf certificate has no 2.5.29.19 extension with cA TRUE, for example with cert.getExtension({id: '2.5.29.19'}). CA operators can avoid copying unrecognized extensions from certificate requests.

Credits

WorkOS Security Team· security@workos.com (@nickcollisson-workos)

Proof of concept

Have a trusted CA issue an end-entity certificate whose extensions are, in this order: a 2.5.29.10 (or 2.5.29.13) extension with the value of a basicConstraints cA TRUE (DER 30 03 01 01 FF), then the CA's own 2.5.29.19 basicConstraints with cA FALSE, and no keyUsage. For example, openssl x509 -req -copy_extensions copy with an extensions file that sets basicConstraints = CA:FALSE copies the extension from the CSR and produces this order. Use that certificate's key to sign a leaf certificate for any name.

On node-forge <= 1.4.0, forge.pki.verifyCertificateChain(caStore, [leaf, eeCert]) accepts the chain, and eeCert.getExtension('basicConstraints') returns the 2.5.29.10 extension with cA true. The same certificate without the deprecated-OID extension is rejected ("Certificate basicConstraints indicates the certificate is not a CA."). A certificate with only the deprecated-OID extension is also accepted.

The fix adds regression tests in tests/unit/x509.js and the reporter's PoC as tests/pocs/ghsa-5x38-2vff-x55p.js.

Impact

Applications that verify certificate chains with forge.pki.verifyCertificateChain(), including TLS peer certificate verification in forge.tls, are affected.

Exploitation requires a CA trusted by the application to issue a certificate that contains the attacker-supplied deprecated-OID extension, typically a CA that copies extensions from certificate signing requests. For example, openssl x509 -req -copy_extensions copy (or copyall) with an extensions file that sets basicConstraints = CA:FALSE and no keyUsage produces such a certificate. If the issuing CA adds a keyUsage extension without keyCertSign, node-forge rejects the chain.

The holder of such a certificate can then issue certificates for any name that pki.verifyCertificateChain() accepts. In forge's own TLS stack this allows impersonating servers (or clients, where client certificates are verified) and intercepting traffic.

Affected versions

npm

PackageVulnerableFixed in
node-forge
<= 1.4.0
No fix yet
node-forge
< 1.4.1
1.4.1

How to fix

Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.

node-forge npm → 1.4.1

npm
npm install node-forge@1.4.1
yarn
yarn add node-forge@1.4.1
pnpm
pnpm add node-forge@1.4.1

Severity breakdown

CVSS 3.1 7.4

CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N

Attack vector
Network
Attack complexity
High
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
None

Weakness

CWE-295: Improper Certificate Validation

The product does not validate, or incorrectly validates, a certificate.

How to prevent it
  • Architecture and Design, Implementation: Certificates should be carefully managed and checked to assure that data are encrypted with the intended owner's public key.
  • Implementation: If certificate pinning is being used, ensure that all relevant properties of the certificate are fully validated before the certificate is pinned, including the hostname.

CWE-436: Interpretation Conflict

Product A handles inputs or steps differently than Product B, which causes A to perform incorrect actions based on its perception of B's state.

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GHSA-4642-5mcj-2rqw
Forge: ASN.1 OID first arc truncation in asn1.oidToDer
Low Also affects node-forge
GHSA-6wvx-3r32-xmcg
Forge: Prototype Pollution in forge.http cookie handling via a malicious Set-Cookie
Medium Also affects node-forge
GHSA-525v-9gp9-w22h
Forge: TLS client crash when the server sends no certificate
Medium Also affects node-forge
GHSA-7wch-p9px-956x
Forge: Regular expression denial of service in PEM decoding (pem.decode)
High Also affects node-forge
GHSA-h6cr-wjv9-3cpp
Forge: Denial of Service via infinite loop in BigInteger.modInverse() with negative input (incomplete fix of CVE-2026-33891)
Low Also affects node-forge
GHSA-4487-j8r4-4g2f
Forge: verifyCertificateChain() removes trusted CA certificates with the same subject from the CA store
Low Also affects node-forge
GHSA-5cwg-6vw7-rj6p
Forge: AES-GCM authentication bypass via unvalidated tagLength option
Medium Also affects node-forge
GHSA-jc66-7q78-782g
Forge: PKCS#12 MAC forgery and constant-key PBE via zero iteration count
High Also affects node-forge
Is this in your code?

MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.

Check your repos with MergeHarbor