NATS has MQTT plaintext password disclosure
Description
NATS-Server is a High-Performance server for NATS.io, a cloud and edge native messaging system. Prior to versions 2.11.15 and 2.12.6, for MQTT deployments using usercodes/passwords: MQTT passwords are incorrectly classified as a non-authenticating identity statement (JWT) and exposed via monitoring endpoints. Versions 2.11.14 and 2.12.6 contain a fix. As a workaround, ensure monitoring end-points are adequately secured. Best practice remains to not expose the monitoring endpoint to the Internet or other untrusted network users.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/nats-io/nats-server/v2Go | < 2.11.15 | 2.11.15 |
github.com/nats-io/nats-server/v2Go | >= 2.12.0-RC.1, < 2.12.6 | 2.12.6 |
github.com/nats-io/nats-serverGo | >= 0 | — |
Affected products
1- Range: < 2.11.15
Patches
1b5b63cfc35a5[FIXED] MQTT password exposed in JWT
2 files changed · +16 −4
server/auth.go+16 −3 modified@@ -604,6 +604,7 @@ func processUserPermissionsTemplate(lim jwt.UserPermissionLimits, ujwt *jwt.User func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) (authorized bool) { var ( nkey *NkeyUser + ujwt string juc *jwt.UserClaims acc *Account user *User @@ -798,16 +799,23 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) (au // Check if we have trustedKeys defined in the server. If so we require a user jwt. if s.trustedKeys != nil { - if c.opts.JWT == _EMPTY_ && opts.DefaultSentinel != _EMPTY_ { + ujwt = c.opts.JWT + if ujwt == _EMPTY_ && c.isMqtt() { + // For MQTT, we pass the password as the JWT too, but do so here so it's not + // publicly exposed in the client options if it isn't a JWT. + ujwt = c.opts.Password + } + if ujwt == _EMPTY_ && opts.DefaultSentinel != _EMPTY_ { c.opts.JWT = opts.DefaultSentinel + ujwt = c.opts.JWT } - if c.opts.JWT == _EMPTY_ { + if ujwt == _EMPTY_ { s.mu.Unlock() c.Debugf("Authentication requires a user JWT") return false } // So we have a valid user jwt here. - juc, err = jwt.DecodeUserClaims(c.opts.JWT) + juc, err = jwt.DecodeUserClaims(ujwt) if err != nil { s.mu.Unlock() c.Debugf("User JWT not valid: %v", err) @@ -1077,6 +1085,11 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) (au // Hold onto the user's public key. c.mu.Lock() c.pubKey = juc.Subject + // If this is a MQTT client, we purposefully didn't populate the JWT as it could contain + // a password or token. Now we know it's a valid JWT, we can populate it. + if c.isMqtt() { + c.opts.JWT = ujwt + } c.tags = juc.Tags c.nameTag = juc.Name c.mu.Unlock()
server/mqtt.go+0 −1 modified@@ -3788,7 +3788,6 @@ func (c *client) mqttParseConnect(r *mqttReader, hasMappings bool) (byte, *mqttC return 0, nil, err } c.opts.Token = c.opts.Password - c.opts.JWT = c.opts.Password } return 0, cp, nil }
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-v722-jcv5-w7mcghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-33216ghsaADVISORY
- advisories.nats.io/CVE/secnote-2026-05.txtghsax_refsource_MISCWEB
- github.com/nats-io/nats-server/commit/b5b63cfc35a57075e09c1f57503d31721bed8099ghsax_refsource_MISCWEB
- github.com/nats-io/nats-server/security/advisories/GHSA-v722-jcv5-w7mcghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.