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.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/zitadel/zitadelGo | >= 2.50.0, < 2.50.3 | 2.50.3 |
github.com/zitadel/zitadelGo | >= 2.49.0, < 2.49.5 | 2.49.5 |
github.com/zitadel/zitadelGo | >= 2.48.0, < 2.48.5 | 2.48.5 |
github.com/zitadel/zitadelGo | >= 2.47.0, < 2.47.10 | 2.47.10 |
github.com/zitadel/zitadelGo | < 2.45.7 | 2.45.7 |
Affected products
1Patches
1b918603b576dfix: exclude db connection error details (#7785)
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, ¬FoundErr) { - 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- github.com/advisories/GHSA-q5qj-x2h5-3945ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-32967ghsaADVISORY
- github.com/zitadel/zitadel/commit/b918603b576d156a08b90917c14c2d019c82ffc6ghsax_refsource_MISCWEB
- github.com/zitadel/zitadel/releases/tag/v2.45.7ghsax_refsource_MISCWEB
- github.com/zitadel/zitadel/releases/tag/v2.46.7ghsax_refsource_MISCWEB
- github.com/zitadel/zitadel/releases/tag/v2.47.10ghsax_refsource_MISCWEB
- github.com/zitadel/zitadel/releases/tag/v2.48.5ghsax_refsource_MISCWEB
- github.com/zitadel/zitadel/releases/tag/v2.49.5ghsax_refsource_MISCWEB
- github.com/zitadel/zitadel/releases/tag/v2.50.3ghsax_refsource_MISCWEB
- github.com/zitadel/zitadel/security/advisories/GHSA-q5qj-x2h5-3945ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.