GHSA-5x38-2vff-x55p: Forge: X.509 basicConstraints bypass in verifyCertificateChain() via deprecated extension OIDs 2.5.29.10 and 2.5.29.13

Published Oct 29, 2026
·
Updated

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 -copyextensions 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 -copyextensions 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.

Other sources

Forge (also called node-forge) is a native implementation of Transport Layer Security in JavaScript. Prior to 1.4.1, node-forge parsed deprecated extension OIDs 2.5.29.10 and 2.5.29.13 as basicConstraints, and pki.verifyCertificateChain() used the first extension with that name. Exploitation requires a CA trusted by the application to issue a certificate carrying an attacker-supplied deprecated-OID extension, commonly because the CA copies extensions from a certificate signing request. By placing a permissive deprecated extension before the genuine 2.5.29.19 basicConstraints extension, the certificate holder could have the certificate accepted as a CA and issue certificates for arbitrary names, enabling server or client impersonation. If the issuing CA adds a keyUsage extension without keyCertSign, node-forge rejects the chain. This issue is fixed in version 1.4.1.

— OSV

Affected Software

2 affected componentsFixes available
npm/node-forge<=1.4.0
1.4.1
npm/node-forge<1.4.1
1.4.1

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade npm/node-forge to a version that resolves this vulnerability.

    Fixed in 1.4.1
  2. Upgrade

    Upgrade node-forge to a version that resolves this vulnerability.

    Fixed in 1.4.1
  3. Configuration

    Do not copy unrecognized extensions from certificate signing requests.

    Certificate Authority issuance copying unrecognized certificate-request extensions = disabled
  4. Compensating control

    For applications using pki.verifyCertificateChain() or forge.tls.createConnection(), provide an options.verify/verify callback that rejects any non-leaf certificate lacking a 2.5.29.19 basicConstraints extension with cA TRUE; for example, check cert.getExtension({id: '2.5.29.19'}).

Event History

Oct 29, 2026
CVE Published
via OSV·07:25 PM
Data Sourced
via OSV·07:25 PM
DescriptionSeverityWeakness
Oct 30, 2026
Advisory Published
via GitHub·11:01 PM
Data Sourced
via GitHub·11:01 PM
DescriptionSeverityWeaknessAffected Software
Data Sourced
via OSV·11:01 PM
Affected Software

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203