{"record":{"id":"a08af51471278b62","repo":"hyperledger/fabric","slug":"session-binding-mismatch","errorCode":null,"errorMessage":"session binding mismatch","messagePattern":"session binding mismatch","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/common/cluster/clusterservice.go","lineNumber":136,"sourceCode":"\t\t// Else, no error occurred, so we continue to the next iteration\n\t}\n}\n\nfunc (s *ClusterService) VerifyAuthRequest(stream orderer.ClusterNodeService_StepServer, request *orderer.ClusterNodeServiceStepRequest) (*orderer.NodeAuthRequest, error) {\n\tauthReq := request.GetNodeAuthrequest()\n\tif authReq == nil {\n\t\treturn nil, errors.New(\"invalid request object\")\n\t}\n\n\tbindingFieldsHash := GetSessionBindingHash(authReq)\n\n\ttlsBinding, err := GetTLSSessionBinding(stream.Context(), bindingFieldsHash)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"session binding read failed\")\n\t}\n\n\tif !bytes.Equal(tlsBinding, authReq.SessionBinding) {\n\t\treturn nil, errors.New(\"session binding mismatch\")\n\t}\n\n\tmsg, err := asn1.Marshal(AuthRequestSignature{\n\t\tVersion:        int64(authReq.Version),\n\t\tTimestamp:      EncodeTimestamp(authReq.Timestamp),\n\t\tFromId:         strconv.FormatUint(authReq.FromId, 10),\n\t\tToId:           strconv.FormatUint(authReq.ToId, 10),\n\t\tSessionBinding: tlsBinding,\n\t\tChannel:        authReq.Channel,\n\t})\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"ASN encoding failed\")\n\t}\n\n\tmembership := s.MembershipByChannel[authReq.Channel]\n\tif membership == nil {\n\t\treturn nil, errors.Errorf(\"channel %s not found in config\", authReq.Channel)\n\t}","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/common/cluster/clusterservice.go#L118-L154","documentation":"After computing the expected TLS binding, VerifyAuthRequest compares it byte-for-byte with authReq.SessionBinding supplied by the client. This error means the client-claimed binding does not match the actual TLS session, i.e. the request may be replayed or sent over a different connection than the one that signed it — an authentication security check.","triggerScenarios":"A client caches/reuses an auth request (and its binding) across new TLS connections; the client computes GetSessionBindingHash with different fields than the server; clock/connection differences such that the binding hash inputs diverge.","commonSituations":"Connection pooling that reuses stale authenticated requests on fresh TLS sessions; replay attempts (legitimate or malicious); differing Fabric versions hashing different fields; wrong channel/endpoint config causing the client to bind against a different context.","solutions":["Ensure the client computes and sends a fresh NodeAuthRequest per TLS connection, re-deriving GetSessionBindingHash from the live connection rather than reusing a cached value.","Upgrade both peers and orderers to matching Fabric versions so the binding-hash algorithm and fields agree.","Check that the TLS client certificate used for signing is the same one presented on the connection.","If using custom/proxied transports, make sure the original TLS session identity reaches the orderer unchanged."],"exampleFix":"// before\nauthReq := cachedAuthReq // reused across connections -> binding mismatch\n// after\nauthReq := cluster.NewAuthRequest(conn, signer) // computed from current TLS session","handlingStrategy":"validation","validationCode":"// client side: derive the binding from the LIVE connection, never cache it\nbindingHash := cluster.GetSessionBindingHash(authReq)\ntlsBinding, err := cluster.GetTLSSessionBinding(ctx, bindingHash)\nif err != nil { return err }\nif !bytes.Equal(tlsBinding, authReq.SessionBinding) {\n    return errors.New(\"client-side binding mismatch: recompute per connection\")\n}","typeGuard":null,"tryCatchPattern":"_, err := svc.VerifyAuthRequest(stream, request)\nif err != nil && strings.Contains(err.Error(), \"session binding mismatch\") {\n    // do NOT retry with the same auth request; regenerate it on a fresh connection\n    return status.Error(codes.Unauthenticated, \"stale session binding: re-authenticate\")\n}","preventionTips":["Never cache NodeAuthRequests across TLS connections (replay protection is by design)","Match Fabric versions so binding hash fields agree on both ends","Sign and send over the same TLS session the binding was computed from","Treat repeated mismatches as a security signal and audit for replay attempts"],"tags":["fabric","orderer","tls","authentication","replay-protection"],"backgroundTag":"tls-session-binding-failed","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}