CVE-2025-64513
Description
Milvus is an open-source vector database built for generative AI applications. An unauthenticated attacker can exploit a vulnerability in versions prior to 2.4.24, 2.5.21, and 2.6.5 to bypass all authentication mechanisms in the Milvus Proxy component, gaining full administrative access to the Milvus cluster. This grants the attacker the ability to read, modify, or delete data, and to perform privileged administrative operations such as database or collection management. This issue has been fixed in Milvus 2.4.24, 2.5.21, and 2.6.5. If immediate upgrade is not possible, a temporary mitigation can be applied by removing the sourceID header from all incoming requests at the gateway, API gateway, or load balancer level before they reach the Milvus Proxy. This prevents attackers from exploiting the authentication bypass behavior.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/milvus-io/milvusGo | >= 0.10.4, < 2.4.24 | 2.4.24 |
github.com/milvus-io/milvusGo | >= 2.5.0, < 2.5.21 | 2.5.21 |
github.com/milvus-io/milvusGo | >= 2.6.0, < 2.6.5 | 2.6.5 |
github.com/milvus-io/milvusGo | < 0.10.3-0.20251107071934-6102f001a971 | 0.10.3-0.20251107071934-6102f001a971 |
Affected products
1Patches
16102f001a971enhance: skip check source id (#45377)
6 files changed · +34 −101
internal/proxy/authentication_interceptor.go+32 −50 modified@@ -33,20 +33,6 @@ func parseMD(rawToken string) (username, password string) { return } -func validSourceID(ctx context.Context, authorization []string) bool { - if len(authorization) < 1 { - // log.Warn("key not found in header", zap.String("key", util.HeaderSourceID)) - return false - } - // token format: base64<sourceID> - token := authorization[0] - sourceID, err := crypto.Base64Decode(token) - if err != nil { - return false - } - return sourceID == util.MemberCredID -} - func GrpcAuthInterceptor(authFunc grpc_auth.AuthFunc) grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { var newCtx context.Context @@ -77,48 +63,44 @@ func AuthenticationInterceptor(ctx context.Context) (context.Context, error) { if globalMetaCache == nil { return nil, merr.WrapErrServiceUnavailable("internal: Milvus Proxy is not ready yet. please wait") } - // check: - // 1. if rpc call from a member (like index/query/data component) - // 2. if rpc call from sdk + // check rpc call from sdk if Params.CommonCfg.AuthorizationEnabled.GetAsBool() { - if !validSourceID(ctx, md[strings.ToLower(util.HeaderSourceID)]) { - authStrArr := md[strings.ToLower(util.HeaderAuthorize)] + authStrArr := md[strings.ToLower(util.HeaderAuthorize)] - if len(authStrArr) < 1 { - log.Warn("key not found in header") - return nil, status.Error(codes.Unauthenticated, "missing authorization in header") - } + if len(authStrArr) < 1 { + log.Warn("key not found in header") + return nil, status.Error(codes.Unauthenticated, "missing authorization in header") + } - // token format: base64<username:password> - // token := strings.TrimPrefix(authorization[0], "Bearer ") - token := authStrArr[0] - rawToken, err := crypto.Base64Decode(token) + // token format: base64<username:password> + // token := strings.TrimPrefix(authorization[0], "Bearer ") + token := authStrArr[0] + rawToken, err := crypto.Base64Decode(token) + if err != nil { + log.Warn("fail to decode the token", zap.Error(err)) + return nil, status.Error(codes.Unauthenticated, "invalid token format") + } + + if !strings.Contains(rawToken, util.CredentialSeperator) { + user, err := VerifyAPIKey(rawToken) if err != nil { - log.Warn("fail to decode the token", zap.Error(err)) - return nil, status.Error(codes.Unauthenticated, "invalid token format") + log.Warn("fail to verify apikey", zap.Error(err)) + return nil, status.Error(codes.Unauthenticated, "auth check failure, please check api key is correct") } - - if !strings.Contains(rawToken, util.CredentialSeperator) { - user, err := VerifyAPIKey(rawToken) - if err != nil { - log.Warn("fail to verify apikey", zap.Error(err)) - return nil, status.Error(codes.Unauthenticated, "auth check failure, please check api key is correct") - } - metrics.UserRPCCounter.WithLabelValues(user).Inc() - userToken := fmt.Sprintf("%s%s%s", user, util.CredentialSeperator, util.PasswordHolder) - md[strings.ToLower(util.HeaderAuthorize)] = []string{crypto.Base64Encode(userToken)} - md[util.HeaderToken] = []string{rawToken} - ctx = metadata.NewIncomingContext(ctx, md) - } else { - // username+password authentication - username, password := parseMD(rawToken) - if !passwordVerify(ctx, username, password, privilege.GetPrivilegeCache()) { - log.Warn("fail to verify password", zap.String("username", username)) - // NOTE: don't use the merr, because it will cause the wrong retry behavior in the sdk - return nil, status.Error(codes.Unauthenticated, "auth check failure, please check username and password are correct") - } - metrics.UserRPCCounter.WithLabelValues(username).Inc() + metrics.UserRPCCounter.WithLabelValues(user).Inc() + userToken := fmt.Sprintf("%s%s%s", user, util.CredentialSeperator, util.PasswordHolder) + md[strings.ToLower(util.HeaderAuthorize)] = []string{crypto.Base64Encode(userToken)} + md[util.HeaderToken] = []string{rawToken} + ctx = metadata.NewIncomingContext(ctx, md) + } else { + // username+password authentication + username, password := parseMD(rawToken) + if !passwordVerify(ctx, username, password, privilege.GetPrivilegeCache()) { + log.Warn("fail to verify password", zap.String("username", username)) + // NOTE: don't use the merr, because it will cause the wrong retry behavior in the sdk + return nil, status.Error(codes.Unauthenticated, "auth check failure, please check username and password are correct") } + metrics.UserRPCCounter.WithLabelValues(username).Inc() } } return ctx, nil
internal/proxy/authentication_interceptor_test.go+0 −18 modified@@ -49,19 +49,6 @@ func TestValidAuth(t *testing.T) { assert.False(t, res) } -func TestValidSourceID(t *testing.T) { - ctx := context.Background() - // no metadata - res := validSourceID(ctx, nil) - assert.False(t, res) - // illegal metadata - res = validSourceID(ctx, []string{"invalid_sourceid"}) - assert.False(t, res) - // normal sourceId - res = validSourceID(ctx, []string{crypto.Base64Encode(util.MemberCredID)}) - assert.True(t, res) -} - func TestAuthenticationInterceptor(t *testing.T) { ctx := context.Background() paramtable.Get().Save(Params.CommonCfg.AuthorizationEnabled.Key, "true") // mock authorization is turned on @@ -83,11 +70,6 @@ func TestAuthenticationInterceptor(t *testing.T) { ctx = metadata.NewIncomingContext(ctx, md) _, err = AuthenticationInterceptor(ctx) assert.NoError(t, err) - // with valid sourceId - md = metadata.Pairs("sourceid", crypto.Base64Encode(util.MemberCredID)) - ctx = metadata.NewIncomingContext(ctx, md) - _, err = AuthenticationInterceptor(ctx) - assert.NoError(t, err) { // wrong authorization style
internal/util/grpcclient/auth.go+0 −19 removed@@ -1,19 +0,0 @@ -package grpcclient - -import ( - "context" - - "github.com/milvus-io/milvus/pkg/v2/util" -) - -type Token struct { - Value string -} - -func (t *Token) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { - return map[string]string{util.HeaderSourceID: t.Value}, nil -} - -func (t *Token) RequireTransportSecurity() bool { - return false -}
internal/util/grpcclient/client.go+0 −4 modified@@ -40,8 +40,6 @@ import ( "github.com/milvus-io/milvus/internal/util/sessionutil" "github.com/milvus-io/milvus/pkg/v2/log" "github.com/milvus-io/milvus/pkg/v2/tracer" - "github.com/milvus-io/milvus/pkg/v2/util" - "github.com/milvus-io/milvus/pkg/v2/util/crypto" "github.com/milvus-io/milvus/pkg/v2/util/funcutil" "github.com/milvus-io/milvus/pkg/v2/util/generic" "github.com/milvus-io/milvus/pkg/v2/util/interceptor" @@ -310,7 +308,6 @@ func (c *ClientBase[T]) connect(ctx context.Context) error { }, MinConnectTimeout: c.DialTimeout, }), - grpc.WithPerRPCCredentials(&Token{Value: crypto.Base64Encode(util.MemberCredID)}), grpc.FailOnNonTempDialError(true), grpc.WithReturnConnectionError(), grpc.WithDisableRetry(), @@ -349,7 +346,6 @@ func (c *ClientBase[T]) connect(ctx context.Context) error { }, MinConnectTimeout: c.DialTimeout, }), - grpc.WithPerRPCCredentials(&Token{Value: crypto.Base64Encode(util.MemberCredID)}), grpc.FailOnNonTempDialError(true), grpc.WithReturnConnectionError(), grpc.WithDisableRetry(),
pkg/util/constant.go+2 −6 modified@@ -44,12 +44,8 @@ const ( SegmentIndexPrefix = "segment-index" FieldIndexPrefix = "field-index" - HeaderAuthorize = "authorization" - HeaderToken = "token" - // HeaderSourceID identify requests from Milvus members and client requests - HeaderSourceID = "sourceId" - // MemberCredID id for Milvus members (data/index/query node/coord component) - MemberCredID = "@@milvus-member@@" + HeaderAuthorize = "authorization" + HeaderToken = "token" CredentialSeperator = ":" UserRoot = "root" PasswordHolder = "___"
tests/integration/cluster/process/milvus_process.go+0 −4 modified@@ -45,12 +45,9 @@ import ( "github.com/milvus-io/milvus-proto/go-api/v2/commonpb" "github.com/milvus-io/milvus-proto/go-api/v2/milvuspb" - "github.com/milvus-io/milvus/internal/util/grpcclient" "github.com/milvus-io/milvus/internal/util/sessionutil" "github.com/milvus-io/milvus/pkg/v2/log" - "github.com/milvus-io/milvus/pkg/v2/util" "github.com/milvus-io/milvus/pkg/v2/util/contextutil" - "github.com/milvus-io/milvus/pkg/v2/util/crypto" "github.com/milvus-io/milvus/pkg/v2/util/interceptor" "github.com/milvus-io/milvus/pkg/v2/util/lifetime" "github.com/milvus-io/milvus/pkg/v2/util/paramtable" @@ -499,7 +496,6 @@ func DailGRPClient(ctx context.Context, addr string, rootPath string, nodeID int }, MinConnectTimeout: 5 * time.Second, }), - grpc.WithPerRPCCredentials(&grpcclient.Token{Value: crypto.Base64Encode(util.MemberCredID)}), grpc.FailOnNonTempDialError(true), grpc.WithReturnConnectionError(), grpc.WithDisableRetry(),
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
7- github.com/advisories/GHSA-mhjq-8c7m-3f7pghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-64513ghsaADVISORY
- github.com/milvus-io/milvus/commit/6102f001a971c8c8055a4a4cae704442d5cab793ghsaWEB
- github.com/milvus-io/milvus/pull/45379nvdWEB
- github.com/milvus-io/milvus/pull/45383nvdWEB
- github.com/milvus-io/milvus/pull/45391nvdWEB
- github.com/milvus-io/milvus/security/advisories/GHSA-mhjq-8c7m-3f7pnvdWEB
News mentions
0No linked articles in our index yet.