VYPR
Moderate severityNVD Advisory· Published May 1, 2024· Updated Aug 2, 2024

Zitadel exposes internal database user name and host information

CVE-2024-32967

Description

Zitadel is an open source identity management system. In case ZITADEL could not connect to the database, connection information including db name, username and db host name could be returned to the user. This has been addressed in all supported release branches in a point release. There is no workaround since a patch is already available. Users are advised to upgrade.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/zitadel/zitadelGo
>= 2.50.0, < 2.50.32.50.3
github.com/zitadel/zitadelGo
>= 2.49.0, < 2.49.52.49.5
github.com/zitadel/zitadelGo
>= 2.48.0, < 2.48.52.48.5
github.com/zitadel/zitadelGo
>= 2.47.0, < 2.47.102.47.10
github.com/zitadel/zitadelGo
< 2.45.72.45.7

Affected products

1

Patches

1
b918603b576d

fix: exclude db connection error details (#7785)

https://github.com/zitadel/zitadelLivio SpringApr 23, 2024via ghsa
8 files changed · +67 38
  • cmd/start/start.go+2 2 modified
    @@ -356,7 +356,7 @@ func startAPIs(
     		http_util.WithMaxAge(int(math.Floor(config.Quotas.Access.ExhaustedCookieMaxAge.Seconds()))),
     	)
     	limitingAccessInterceptor := middleware.NewAccessInterceptor(accessSvc, exhaustedCookieHandler, &config.Quotas.Access.AccessConfig)
    -	apis, err := api.New(ctx, config.Port, router, queries, verifier, config.InternalAuthZ, tlsConfig, config.HTTP2HostHeader, config.HTTP1HostHeader, limitingAccessInterceptor)
    +	apis, err := api.New(ctx, config.Port, router, queries, verifier, config.InternalAuthZ, tlsConfig, config.HTTP2HostHeader, config.HTTP1HostHeader, config.ExternalDomain, limitingAccessInterceptor)
     	if err != nil {
     		return fmt.Errorf("error creating api %w", err)
     	}
    @@ -400,7 +400,7 @@ func startAPIs(
     	if err := apis.RegisterService(ctx, org.CreateServer(commands, queries, permissionCheck)); err != nil {
     		return err
     	}
    -	instanceInterceptor := middleware.InstanceInterceptor(queries, config.HTTP1HostHeader, login.IgnoreInstanceEndpoints...)
    +	instanceInterceptor := middleware.InstanceInterceptor(queries, config.HTTP1HostHeader, config.ExternalDomain, login.IgnoreInstanceEndpoints...)
     	assetsCache := middleware.AssetsCacheInterceptor(config.AssetStorage.Cache.MaxAge, config.AssetStorage.Cache.SharedMaxAge)
     	apis.RegisterHandlerOnPrefix(assets.HandlerPrefix, assets.NewHandler(commands, verifier, config.InternalAuthZ, id.SonyFlakeGenerator(), store, queries, middleware.CallDurationHandler, instanceInterceptor.Handler, assetsCache.Handler, limitingAccessInterceptor.Handle))
     
    
  • internal/api/api.go+2 2 modified
    @@ -49,7 +49,7 @@ func New(
     	queries *query.Queries,
     	verifier internal_authz.APITokenVerifier,
     	authZ internal_authz.Config,
    -	tlsConfig *tls.Config, http2HostName, http1HostName string,
    +	tlsConfig *tls.Config, http2HostName, http1HostName, externalDomain string,
     	accessInterceptor *http_mw.AccessInterceptor,
     ) (_ *API, err error) {
     	api := &API{
    @@ -62,7 +62,7 @@ func New(
     		accessInterceptor: accessInterceptor,
     	}
     
    -	api.grpcServer = server.CreateServer(api.verifier, authZ, queries, http2HostName, tlsConfig, accessInterceptor.AccessService())
    +	api.grpcServer = server.CreateServer(api.verifier, authZ, queries, http2HostName, externalDomain, tlsConfig, accessInterceptor.AccessService())
     	api.grpcGateway, err = server.CreateGateway(ctx, port, http1HostName, accessInterceptor, tlsConfig)
     	if err != nil {
     		return nil, err
    
  • internal/api/assets/asset.go+21 7 modified
    @@ -2,6 +2,7 @@ package assets
     
     import (
     	"context"
    +	"errors"
     	"fmt"
     	"io"
     	"net/http"
    @@ -12,14 +13,17 @@ import (
     	"github.com/gabriel-vasile/mimetype"
     	"github.com/gorilla/mux"
     	"github.com/zitadel/logging"
    +	"golang.org/x/text/language"
     
     	"github.com/zitadel/zitadel/internal/api/authz"
     	http_util "github.com/zitadel/zitadel/internal/api/http"
     	http_mw "github.com/zitadel/zitadel/internal/api/http/middleware"
     	"github.com/zitadel/zitadel/internal/command"
    +	"github.com/zitadel/zitadel/internal/i18n"
     	"github.com/zitadel/zitadel/internal/id"
     	"github.com/zitadel/zitadel/internal/query"
     	"github.com/zitadel/zitadel/internal/static"
    +	"github.com/zitadel/zitadel/internal/zerrors"
     )
     
     const (
    @@ -73,19 +77,29 @@ type Downloader interface {
     
     type ErrorHandler func(w http.ResponseWriter, r *http.Request, err error, defaultCode int)
     
    -func DefaultErrorHandler(w http.ResponseWriter, r *http.Request, err error, defaultCode int) {
    -	logging.WithFields("uri", r.RequestURI).WithError(err).Warn("error occurred on asset api")
    -	code, ok := http_util.ZitadelErrorToHTTPStatusCode(err)
    -	if !ok {
    -		code = defaultCode
    +func DefaultErrorHandler(translator *i18n.Translator) func(w http.ResponseWriter, r *http.Request, err error, defaultCode int) {
    +	return func(w http.ResponseWriter, r *http.Request, err error, defaultCode int) {
    +		logging.WithFields("uri", r.RequestURI).WithError(err).Warn("error occurred on asset api")
    +		code, ok := http_util.ZitadelErrorToHTTPStatusCode(err)
    +		if !ok {
    +			code = defaultCode
    +		}
    +		zErr := new(zerrors.ZitadelError)
    +		if errors.As(err, &zErr) {
    +			zErr.SetMessage(translator.LocalizeFromCtx(r.Context(), zErr.GetMessage(), nil))
    +			zErr.Parent = nil // ensuring we don't leak any unwanted information
    +			err = zErr
    +		}
    +		http.Error(w, err.Error(), code)
     	}
    -	http.Error(w, err.Error(), code)
     }
     
     func NewHandler(commands *command.Commands, verifier authz.APITokenVerifier, authConfig authz.Config, idGenerator id.Generator, storage static.Storage, queries *query.Queries, callDurationInterceptor, instanceInterceptor, assetCacheInterceptor, accessInterceptor func(handler http.Handler) http.Handler) http.Handler {
    +	translator, err := i18n.NewZitadelTranslator(language.English)
    +	logging.OnError(err).Panic("unable to get translator")
     	h := &Handler{
     		commands:        commands,
    -		errorHandler:    DefaultErrorHandler,
    +		errorHandler:    DefaultErrorHandler(translator),
     		authInterceptor: http_mw.AuthorizationInterceptor(verifier, authConfig),
     		idGenerator:     idGenerator,
     		storage:         storage,
    
  • internal/api/grpc/gerrors/zitadel_errors.go+4 0 modified
    @@ -2,6 +2,7 @@ package gerrors
     
     import (
     	"errors"
    +	"strings"
     
     	"github.com/zitadel/logging"
     	"google.golang.org/grpc/codes"
    @@ -35,6 +36,9 @@ func ExtractZITADELError(err error) (c codes.Code, msg, id string, ok bool) {
     	if err == nil {
     		return codes.OK, "", "", false
     	}
    +	if strings.Contains(err.Error(), "failed to connect to") { // version of pgx does not yet export the error type
    +		return codes.Internal, "db connection error", "", true
    +	}
     	zitadelErr := new(zerrors.ZitadelError)
     	if ok := errors.As(err, &zitadelErr); !ok {
     		return codes.Unknown, err.Error(), "", false
    
  • internal/api/grpc/server/middleware/instance_interceptor.go+12 8 modified
    @@ -14,6 +14,7 @@ import (
     	"google.golang.org/grpc/status"
     
     	"github.com/zitadel/zitadel/internal/api/authz"
    +	zitadel_http "github.com/zitadel/zitadel/internal/api/http"
     	"github.com/zitadel/zitadel/internal/i18n"
     	"github.com/zitadel/zitadel/internal/telemetry/tracing"
     	"github.com/zitadel/zitadel/internal/zerrors"
    @@ -23,15 +24,15 @@ const (
     	HTTP1Host = "x-zitadel-http1-host"
     )
     
    -func InstanceInterceptor(verifier authz.InstanceVerifier, headerName string, explicitInstanceIdServices ...string) grpc.UnaryServerInterceptor {
    +func InstanceInterceptor(verifier authz.InstanceVerifier, headerName, externalDomain string, explicitInstanceIdServices ...string) grpc.UnaryServerInterceptor {
     	translator, err := i18n.NewZitadelTranslator(language.English)
     	logging.OnError(err).Panic("unable to get translator")
     	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
    -		return setInstance(ctx, req, info, handler, verifier, headerName, translator, explicitInstanceIdServices...)
    +		return setInstance(ctx, req, info, handler, verifier, headerName, externalDomain, translator, explicitInstanceIdServices...)
     	}
     }
     
    -func setInstance(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, verifier authz.InstanceVerifier, headerName string, translator *i18n.Translator, idFromRequestsServices ...string) (_ interface{}, err error) {
    +func setInstance(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, verifier authz.InstanceVerifier, headerName, externalDomain string, translator *i18n.Translator, idFromRequestsServices ...string) (_ interface{}, err error) {
     	interceptorCtx, span := tracing.NewServerInterceptorSpan(ctx)
     	defer func() { span.EndWithError(err) }()
     	for _, service := range idFromRequestsServices {
    @@ -55,18 +56,21 @@ func setInstance(ctx context.Context, req interface{}, info *grpc.UnaryServerInf
     			return handler(authz.WithInstance(ctx, instance), req)
     		}
     	}
    -
     	host, err := hostFromContext(interceptorCtx, headerName)
     	if err != nil {
     		return nil, status.Error(codes.NotFound, err.Error())
     	}
     	instance, err := verifier.InstanceByHost(interceptorCtx, host)
     	if err != nil {
    -		notFoundErr := new(zerrors.NotFoundError)
    -		if errors.As(err, &notFoundErr) {
    -			notFoundErr.Message = translator.LocalizeFromCtx(ctx, notFoundErr.GetMessage(), nil)
    +		origin := zitadel_http.ComposedOrigin(ctx)
    +		logging.WithFields("origin", origin, "externalDomain", externalDomain).WithError(err).Error("unable to set instance")
    +		zErr := new(zerrors.ZitadelError)
    +		if errors.As(err, &zErr) {
    +			zErr.SetMessage(translator.LocalizeFromCtx(ctx, zErr.GetMessage(), nil))
    +			zErr.Parent = err
    +			return nil, status.Error(codes.NotFound, fmt.Sprintf("unable to set instance using origin %s (ExternalDomain is %s): %s", origin, externalDomain, zErr))
     		}
    -		return nil, status.Error(codes.NotFound, err.Error())
    +		return nil, status.Error(codes.NotFound, fmt.Sprintf("unable to set instance using origin %s (ExternalDomain is %s)", origin, externalDomain))
     	}
     	span.End()
     	return handler(authz.WithInstance(ctx, instance), req)
    
  • internal/api/grpc/server/middleware/instance_interceptor_test.go+8 7 modified
    @@ -78,12 +78,13 @@ func Test_hostNameFromContext(t *testing.T) {
     
     func Test_setInstance(t *testing.T) {
     	type args struct {
    -		ctx        context.Context
    -		req        interface{}
    -		info       *grpc.UnaryServerInfo
    -		handler    grpc.UnaryHandler
    -		verifier   authz.InstanceVerifier
    -		headerName string
    +		ctx            context.Context
    +		req            interface{}
    +		info           *grpc.UnaryServerInfo
    +		handler        grpc.UnaryHandler
    +		verifier       authz.InstanceVerifier
    +		headerName     string
    +		externalDomain string
     	}
     	type res struct {
     		want interface{}
    @@ -136,7 +137,7 @@ func Test_setInstance(t *testing.T) {
     	}
     	for _, tt := range tests {
     		t.Run(tt.name, func(t *testing.T) {
    -			got, err := setInstance(tt.args.ctx, tt.args.req, tt.args.info, tt.args.handler, tt.args.verifier, tt.args.headerName, nil)
    +			got, err := setInstance(tt.args.ctx, tt.args.req, tt.args.info, tt.args.handler, tt.args.verifier, tt.args.headerName, "", nil)
     			if (err != nil) != tt.res.err {
     				t.Errorf("setInstance() error = %v, wantErr %v", err, tt.res.err)
     				return
    
  • internal/api/grpc/server/server.go+2 1 modified
    @@ -39,6 +39,7 @@ func CreateServer(
     	authConfig authz.Config,
     	queries *query.Queries,
     	hostHeaderName string,
    +	externalDomain string,
     	tlsConfig *tls.Config,
     	accessSvc *logstore.Service[*record.AccessLog],
     ) *grpc.Server {
    @@ -50,7 +51,7 @@ func CreateServer(
     				middleware.DefaultTracingServer(),
     				middleware.MetricsHandler(metricTypes, grpc_api.Probes...),
     				middleware.NoCacheInterceptor(),
    -				middleware.InstanceInterceptor(queries, hostHeaderName, system_pb.SystemService_ServiceDesc.ServiceName, healthpb.Health_ServiceDesc.ServiceName),
    +				middleware.InstanceInterceptor(queries, hostHeaderName, externalDomain, system_pb.SystemService_ServiceDesc.ServiceName, healthpb.Health_ServiceDesc.ServiceName),
     				middleware.AccessStorageInterceptor(accessSvc),
     				middleware.ErrorHandler(),
     				middleware.LimitsInterceptor(system_pb.SystemService_ServiceDesc.ServiceName),
    
  • internal/api/http/middleware/instance_interceptor.go+16 11 modified
    @@ -19,16 +19,17 @@ import (
     )
     
     type instanceInterceptor struct {
    -	verifier        authz.InstanceVerifier
    -	headerName      string
    -	ignoredPrefixes []string
    -	translator      *i18n.Translator
    +	verifier                   authz.InstanceVerifier
    +	headerName, externalDomain string
    +	ignoredPrefixes            []string
    +	translator                 *i18n.Translator
     }
     
    -func InstanceInterceptor(verifier authz.InstanceVerifier, headerName string, ignoredPrefixes ...string) *instanceInterceptor {
    +func InstanceInterceptor(verifier authz.InstanceVerifier, headerName, externalDomain string, ignoredPrefixes ...string) *instanceInterceptor {
     	return &instanceInterceptor{
     		verifier:        verifier,
     		headerName:      headerName,
    +		externalDomain:  externalDomain,
     		ignoredPrefixes: ignoredPrefixes,
     		translator:      newZitadelTranslator(),
     	}
    @@ -55,11 +56,15 @@ func (a *instanceInterceptor) handleInstance(w http.ResponseWriter, r *http.Requ
     	}
     	ctx, err := setInstance(r, a.verifier, a.headerName)
     	if err != nil {
    -		caosErr := new(zerrors.NotFoundError)
    -		if errors.As(err, &caosErr) {
    -			caosErr.Message = a.translator.LocalizeFromRequest(r, caosErr.GetMessage(), nil)
    +		origin := zitadel_http.ComposedOrigin(r.Context())
    +		logging.WithFields("origin", origin, "externalDomain", a.externalDomain).WithError(err).Error("unable to set instance")
    +		zErr := new(zerrors.ZitadelError)
    +		if errors.As(err, &zErr) {
    +			zErr.SetMessage(a.translator.LocalizeFromRequest(r, zErr.GetMessage(), nil))
    +			http.Error(w, fmt.Sprintf("unable to set instance using origin %s (ExternalDomain is %s): %s", origin, a.externalDomain, zErr), http.StatusNotFound)
    +			return
     		}
    -		http.Error(w, err.Error(), http.StatusNotFound)
    +		http.Error(w, fmt.Sprintf("unable to set instance using origin %s (ExternalDomain is %s)", origin, a.externalDomain), http.StatusNotFound)
     		return
     	}
     	r = r.WithContext(ctx)
    @@ -68,13 +73,13 @@ func (a *instanceInterceptor) handleInstance(w http.ResponseWriter, r *http.Requ
     
     func setInstance(r *http.Request, verifier authz.InstanceVerifier, headerName string) (_ context.Context, err error) {
     	ctx := r.Context()
    -
     	authCtx, span := tracing.NewServerInterceptorSpan(ctx)
     	defer func() { span.EndWithError(err) }()
     
     	host, err := HostFromRequest(r, headerName)
    +
     	if err != nil {
    -		return nil, zerrors.ThrowNotFound(err, "INST-zWq7X", "Errors.Instance.NotFound")
    +		return nil, zerrors.ThrowNotFound(err, "INST-zWq7X", "Errors.IAM.NotFound")
     	}
     
     	instance, err := verifier.InstanceByHost(authCtx, host)
    

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

10

News mentions

0

No linked articles in our index yet.