VYPR
High severityNVD Advisory· Published Mar 25, 2026· Updated Mar 28, 2026

NATS has MQTT plaintext password disclosure

CVE-2026-33216

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.

PackageAffected versionsPatched versions
github.com/nats-io/nats-server/v2Go
< 2.11.152.11.15
github.com/nats-io/nats-server/v2Go
>= 2.12.0-RC.1, < 2.12.62.12.6
github.com/nats-io/nats-serverGo
>= 0

Affected products

1

Patches

1
b5b63cfc35a5

[FIXED] MQTT password exposed in JWT

https://github.com/nats-io/nats-serverMaurice van VeenMar 12, 2026via ghsa
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

News mentions

0

No linked articles in our index yet.