grpc/grpc-go · error

requires SecurityLevel

Error message

requires SecurityLevel %v; connection has %v

What it means

Returned by credentials.CheckSecurityLevel when the established connection's CommonAuthInfo.SecurityLevel is lower than the level the caller required. SecurityLevel ordering is NoSecurity(1) < IntegrityOnly(2) < PrivacyAndIntegrity(3). Per-RPC credential implementations call CheckSecurityLevel to refuse sending secrets over an inadequately protected transport.

Solutions

  1. Use a transport credential that provides the required level: TLS (credentials.NewTLS) or ALTS (alts.NewClientCreds/NewServerCreds) which both provide PrivacyAndIntegrity.
  2. Do not pair token/per-RPC credentials with insecure.NewCredentials(); RequireTransportSecurity()==true exists precisely to prevent this.
  3. If implementing custom TransportCredentials, set CommonAuthInfo.SecurityLevel accurately in your AuthInfo struct.
  4. Lower the required level only if you have verified the data sensitivity allows it.

Example fix

// before: token creds over insecure transport -> CheckSecurityLevel fails
import "google.golang.org/grpc/credentials/insecure"
creds, _ := google.NewServiceAccountIdentityCredentials(ctx, aud)
conn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithPerRPCCredentials(creds))

// after: use TLS so the connection reports PrivacyAndIntegrity
tlsCreds := credentials.NewTLS(&tls.Config{})
conn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(tlsCreds), grpc.WithPerRPCCredentials(creds))
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the transport provides PrivacyAndIntegrity before attaching token creds.
func ensureSecureTransport(creds credentials.TransportCredentials) error {
    // ALTS and TLS both report PrivacyAndIntegrity; insecure does not.
    // There is no public field to inspect; enforce by policy:
    if _, ok := creds.(*credentials.TlsCapableCreds); ok { return nil }
    // For ALTS, trust by type/name:
    return nil // best practice: never pair per-RPC creds with insecure.NewCredentials()
}

// Stronger runtime check: call CheckSecurityLevel with the connection's AuthInfo
// before sending secrets:
// err := credentials.CheckSecurityLevel(peer.AuthInfo, credentials.PrivacyAndIntegrity)

Type guard

func isTransportSecure(info credentials.ProtocolInfo) bool {
    // ALTS reports SecurityProtocol="alts"; TLS reports "tls".
    return info.SecurityProtocol == "tls" || info.SecurityProtocol == "alts"
}

Try / catch

// In GetRequestMetadata-style code, return a clear error instead of leaking tokens.
if err := credentials.CheckSecurityLevel(ai, credentials.PrivacyAndIntegrity); err != nil {
    return nil, fmt.Errorf("refusing to send credentials over insecure transport: %w", err)
}

Prevention

When it happens

Trigger: A PerRPCCredentials implementation (e.g. the GCP service-account-identity credentials at gcp_service_account_identity_credentials.go:129) calls CheckSecurityLevel(ai, PrivacyAndIntegrity) and the connection's AuthInfo reports a lower level. Also surfaces anywhere a user manually calls CheckSecurityLevel with a level above what the current transport provides.

Common situations: Using per-RPC token/OAuth credentials over an insecure channel (grpc.WithInsecure / insecure.NewCredentials) instead of TLS or ALTS; using IntegrityOnly credentials (e.g. certain ALTS configs) when the caller requires PrivacyAndIntegrity; a custom TransportCredentials that fails to set SecurityLevel correctly (leaving it at a low value).

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/675f42d49c5a0d1d. Report an issue: GitHub.

Appendix: source

Thrown at credentials/credentials.go:303

// CheckSecurityLevel checks if a connection's security level is greater than or equal to the specified one.
// It returns success if 1) the condition is satisfied or 2) AuthInfo struct does not implement GetCommonAuthInfo() method
// or 3) CommonAuthInfo.SecurityLevel has an invalid zero value. For 2) and 3), it is for the purpose of backward-compatibility.
//
// This API is experimental.
func CheckSecurityLevel(ai AuthInfo, level SecurityLevel) error {
	type internalInfo interface {
		GetCommonAuthInfo() CommonAuthInfo
	}
	if ai == nil {
		return errors.New("AuthInfo is nil")
	}
	if ci, ok := ai.(internalInfo); ok {
		// CommonAuthInfo.SecurityLevel has an invalid value.
		if ci.GetCommonAuthInfo().SecurityLevel == InvalidSecurityLevel {
			return nil
		}
		if ci.GetCommonAuthInfo().SecurityLevel < level {
			return fmt.Errorf("requires SecurityLevel %v; connection has %v", level, ci.GetCommonAuthInfo().SecurityLevel)
		}
	}
	// The condition is satisfied or AuthInfo struct does not implement GetCommonAuthInfo() method.
	return nil
}

// ChannelzSecurityInfo defines the interface that security protocols should implement
// in order to provide security info to channelz.
//
// This API is experimental.
type ChannelzSecurityInfo interface {
	GetSecurityValue() ChannelzSecurityValue
}

// ChannelzSecurityValue defines the interface that GetSecurityValue() return value
// should satisfy. This interface should only be satisfied by *TLSChannelzSecurityValue
// and *OtherChannelzSecurityValue.
//

View on GitHub (pinned to 0c51461d27)