hyperledger/fabric · error

session binding mismatch

Error message

session binding mismatch

What it means

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.

Source

Thrown at orderer/common/cluster/clusterservice.go:136

		// Else, no error occurred, so we continue to the next iteration
	}
}

func (s *ClusterService) VerifyAuthRequest(stream orderer.ClusterNodeService_StepServer, request *orderer.ClusterNodeServiceStepRequest) (*orderer.NodeAuthRequest, error) {
	authReq := request.GetNodeAuthrequest()
	if authReq == nil {
		return nil, errors.New("invalid request object")
	}

	bindingFieldsHash := GetSessionBindingHash(authReq)

	tlsBinding, err := GetTLSSessionBinding(stream.Context(), bindingFieldsHash)
	if err != nil {
		return nil, errors.Wrap(err, "session binding read failed")
	}

	if !bytes.Equal(tlsBinding, authReq.SessionBinding) {
		return nil, errors.New("session binding mismatch")
	}

	msg, err := asn1.Marshal(AuthRequestSignature{
		Version:        int64(authReq.Version),
		Timestamp:      EncodeTimestamp(authReq.Timestamp),
		FromId:         strconv.FormatUint(authReq.FromId, 10),
		ToId:           strconv.FormatUint(authReq.ToId, 10),
		SessionBinding: tlsBinding,
		Channel:        authReq.Channel,
	})
	if err != nil {
		return nil, errors.Wrap(err, "ASN encoding failed")
	}

	membership := s.MembershipByChannel[authReq.Channel]
	if membership == nil {
		return nil, errors.Errorf("channel %s not found in config", authReq.Channel)
	}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. 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.
  2. Upgrade both peers and orderers to matching Fabric versions so the binding-hash algorithm and fields agree.
  3. Check that the TLS client certificate used for signing is the same one presented on the connection.
  4. If using custom/proxied transports, make sure the original TLS session identity reaches the orderer unchanged.

Example fix

// before
authReq := cachedAuthReq // reused across connections -> binding mismatch
// after
authReq := cluster.NewAuthRequest(conn, signer) // computed from current TLS session
Defensive patterns

Strategy: validation

Validate before calling

// client side: derive the binding from the LIVE connection, never cache it
bindingHash := cluster.GetSessionBindingHash(authReq)
tlsBinding, err := cluster.GetTLSSessionBinding(ctx, bindingHash)
if err != nil { return err }
if !bytes.Equal(tlsBinding, authReq.SessionBinding) {
    return errors.New("client-side binding mismatch: recompute per connection")
}

Try / catch

_, err := svc.VerifyAuthRequest(stream, request)
if err != nil && strings.Contains(err.Error(), "session binding mismatch") {
    // do NOT retry with the same auth request; regenerate it on a fresh connection
    return status.Error(codes.Unauthenticated, "stale session binding: re-authenticate")
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/a08af51471278b62. Report an issue: GitHub.