GHSA-mq36-523m-x7vv: High severity npm/node-opcua vulnerability

Published Aug 20, 2026
·
Updated

Summary A missing nonce verification in the UserNameIdentityToken authentication handler allows an unauthenticated remote attacker to forge a password token that extracts as an empty string, and to replay captured authentication tokens across sessions.

Affected versions: <= 2.165.0 Tested version: 2.165.0 CVSS Score: 8.1 (High) CVSS Vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:L CWE: CWE-347 Improper Verification of Cryptographic Signature

---

Root Cause

In packages/node-opcua-server/source/opcuaserver.ts at line 1886-1887, after RSA-OAEP decrypting the UserNameIdentityToken password blob, the server reads a 4-byte little-endian length field and extracts buff[4 : 4+length] as the password. It never verifies that the trailing bytes equal session.nonce.

This has two consequences:

1. Forged empty password: An attacker who retrieves the server's public key via an unauthenticated GetEndpoints call can craft a token where the 4-byte length field equals serverNonce.length (32). The server computes length = 32 - 32 = 0 and calls isValidUser(username, ""). Any account that accepts an empty password is compromised.

2. Unconditional replay attack: Because nonce binding is structurally absent, any captured UserNameIdentityToken ciphertext can be replayed in a different session unconditionally.

The impact is compounded by a second issue: when the channel uses SecurityMode=None, verifyClientSignature returns true unconditionally (securitypolicy.ts:697-700), bypassing the channel-level signature check entirely.

---

Proof of Concept (logic, no exploit code)

1. GetEndpoints (unauthenticated) → retrieve server public key and RSA token policy 2. OpenSecureChannel (SecurityMode=None) 3. CreateSession 4. Craft plaintext: [0x20, 0x00, 0x00, 0x00] (readUInt32LE = 32 = serverNonce.length) 5. RSA-OAEP encrypt with server public key → 256-byte ciphertext 6. ActivateSession with crafted UserNameIdentityToken 7. Server decrypts → length = 32 - 32 = 0 → password = "" 8. isValidUser(username, "") is called

Dynamically confirmed: decryption produces password = "" with no error and no nonce verification.

---

Suggested Fix

After decrypting the password blob, verify that buff.slice(4 + passwordLength) equals session.nonce before extracting the password. Reject the token if verification fails.

---

I am following a 90-day responsible disclosure policy. I am happy to provide additional technical details under embargo. Please confirm receipt at your earliest convenience.

Reporter: Stanley Tobias Discovery date: 2026-03-23

Affected Software

1 affected component
npm/node-opcua<=2.165.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Configuration

    When OpenSecureChannel uses SecurityMode=None, do not allow security_policy.ts (lines ~697-700) to have verifyClientSignature return true unconditionally; ensure the channel-level signature verification is not bypassed.

    OpenSecureChannel (SecurityMode=None) verifyClientSignature behavior = false (do not unconditionally return true)
  2. Configuration

    After RSA-OAEP decrypts the UserNameIdentityToken password blob (opcua_server.ts line ~1886-1887), read the 4-byte little-endian length and extract the password bytes, then verify the trailing bytes match session.nonce (i.e., reject the token if buff.slice(4 + passwordLength) != session.nonce) before extracting/using the password.

    UserNameIdentityToken authentication handler (packages/node-opcua-server/source/opcua_server.ts) nonce verification for decrypted password blob = buff.slice(4 + passwordLength) must equal session.nonce

Event History

Aug 20, 2026
Advisory Published
via GitHub·06:35 PM
Data Sourced
via GitHub·06:35 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

Which deployments face the most immediate account-compromise risk?

Servers using UserNameIdentityToken authentication are exposed to token replay across sessions. Accounts whose validation logic accepts an empty password are also vulnerable to unauthenticated login with a forged token.

2

What must an attacker obtain to exploit the two identified attack paths?

For the empty-password forgery, an unauthenticated attacker can retrieve the server public key through GetEndpoints and craft a password token. For replay, the attacker needs a captured authentication token that can be reused across sessions.

3

How can I determine whether my node-opcua deployment is affected?

Versions 2.165.0 and earlier are affected, including the tested 2.165.0 release. Review whether the server accepts UserNameIdentityToken credentials and whether any configured account or custom validation path permits an empty password.

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