gcp-cloudsql-explicit-ssl-cert-exposes-credential-explicit

cloudsql.sslCerts.create returns a client SSL certificate private key (one-time in the API response), which serves as a database authentication credential.

explicit gcp emits CanReadCredential

match (effective permission)

{ "action": "cloudsql.sslCerts.create", "resource_type": "google.sql.Instance" }

where

the principal calls cloudsql.sslCerts.create and receives the private key in the API response (certPrivateKey field)

emit

source typeIdentity
target typeCredential
source<principal>
target<SSL Certificate (private key)>
permissionscloudsql.sslCerts.create
conditionsiam_permission
state logicPOTENTIAL when cloudsql.sslCerts.create is confirmed effective. The attacker can CREATE a client certificate and receive the private key in the API response (a one-time event). The key is NOT retrievable after creation (sslCerts.get/list do not return the private key). CONDITIONAL(iam_permission) when the permission is uncertain; BLOCKED when VPC-SC perimeter blocks sqladmin.googleapis.com.

Narrative

{principal.name} holds cloudsql.sslCerts.create on instance {instance.name} and can create a client SSL certificate, receiving the private key in the API response. The private key serves as a database authentication credential.

Raw rule rules/explicit/gcp-cloudsql.yaml

id: gcp-cloudsql-explicit-ssl-cert-exposes-credential-explicit
emits: CanReadCredential
description: cloudsql.sslCerts.create returns a client SSL certificate private key (one-time in the API
  response), which serves as a database authentication credential.
applies_to:
- gcp
match_effective_permission:
  action: cloudsql.sslCerts.create
  resource_type: google.sql.Instance
where:
- the principal calls cloudsql.sslCerts.create and receives the private key in the API response (certPrivateKey
  field)
emit:
  source_type: Identity
  target_type: Credential
  source: <principal>
  target: <SSL Certificate (private key)>
  permissions:
  - cloudsql.sslCerts.create
  conditions:
  - iam_permission
  state_logic: POTENTIAL when cloudsql.sslCerts.create is confirmed effective. The attacker can CREATE
    a client certificate and receive the private key in the API response (a one-time event). The key is
    NOT retrievable after creation (sslCerts.get/list do not return the private key). CONDITIONAL(iam_permission)
    when the permission is uncertain; BLOCKED when VPC-SC perimeter blocks sqladmin.googleapis.com.
  false_positive_note: 'The private key is returned ONLY at cert creation time in the API response field
    certPrivateKey as a one-shot event. Listing existing certs (sslCerts.list/get) returns only the public
    certificate and common name, NOT the private key. This is a POTENTIAL edge (attacker can create the
    cert and get the key), not ACTIVE (which would imply persistent re-access). After obtaining the private
    key, the attacker still must satisfy DB-level cert auth requirements: - MySQL: REQUIRE X509 clause
    on the user account (the CN is NOT a username in MySQL) - PostgreSQL: pg_hba.conf cert method + CN
    mapped to a DB user account These DB-level configs are not visible via the sqladmin API; they must
    be confirmed via data-plane queries or application documentation. The Certificate node represents
    the exposed credential. Refer to credential-chains.yaml for the downstream CredentialsFor -> CanReadData
    roll-up (PostgreSQL-specific auth paths).'
  narrative: '{principal.name} holds cloudsql.sslCerts.create on instance {instance.name} and can create
    a client SSL certificate, receiving the private key in the API response. The private key serves as
    a database authentication credential.'
  api_source: sqladmin.sslCerts.create response (certPrivateKey field)
  evidence_field: cloudsql.sslCerts.create permission + API response certPrivateKey field present
  derived_from:
  - cloudsql.sslCerts.create effective permission on google.sql.Instance
move · open · esc close