
Disabled TLS Certificate Verification for HashiCorp Vault KMS in confluent-kafka
Severity: High, CVSS 3.1 7.4
Vector (v3.1): CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N
Affected: confluent-kafka >= 2.8.0, <= 2.14.2
Fixed in: 2.15.0
CWE: CWE-295 (Improper Certificate Validation)
Component: confluent_kafka/schema_registry/rules/encryption/hcvault/hcvault_client.py
Reported by: Rahul Karne
CNA: VulnCheck
The HashiCorp Vault KMS client in confluent-kafka hardcodes verify=False when constructing its hvac.Client, disabling TLS certificate verification for Vault HTTPS connections made through the Schema Registry field-encryption rules.
The vulnerable code is located in:
confluent_kafka/schema_registry/rules/encryption/hcvault/hcvault_client.py
The affected client is initialized as follows:
self._client = hvac.Client(
url=vault_url,
token=token,
namespace=ns,
verify=False
)
Because verify=False is unconditional, the client accepts untrusted TLS certificates, including self-signed or attacker-controlled certificates.
A network-positioned attacker capable of intercepting or redirecting the application's Vault traffic can impersonate the Vault server, capture Vault authentication credentials, and return forged Vault/KMS responses.
The affected HCVault integration exposes configuration for Vault tokens, namespaces, and AppRole credentials, but affected versions provide no supported CA-bundle configuration or equivalent mechanism to restore certificate verification.
An attacker with a network man-in-the-middle position between the application and HashiCorp Vault can:
X-Vault-Tokenrole_idsecret_idThis can compromise the confidentiality and integrity of data protected by the Vault-backed KMS workflow.
confluent-kafka is the official Confluent Python client for Apache Kafka.
The vulnerability specifically affects deployments using:
Schema Registry encryption rules
↓
HCVault KMS integration
↓
hcvault:// key URI
The affected population is therefore narrower than all confluent-kafka users.
However, the affected functionality is security-sensitive because these deployments rely on HashiCorp Vault for cryptographic key management and field-level encryption.
HcVaultKmsClient.__init__() parses an hcvault:// key URI, determines the Vault base URL, and creates an hvac.Client.
In affected versions, including 2.14.2, the relevant code is:
self._client = hvac.Client(
url=vault_url,
token=token,
namespace=ns,
verify=False
)
if role_id and secret_id and self._client is not None:
self._client.auth.approle.login(
role_id=role_id,
secret_id=secret_id
)
The hvac library ultimately relies on the Python requests TLS stack.
Setting:
verify=False
instructs the client not to validate the remote server's TLS certificate.
As a result, certificates that would normally be rejected can be accepted, including certificates that are:
This affects both supported authentication paths.
When token authentication is used, the Vault token is sent through the:
X-Vault-Token
HTTP header.
If an attacker successfully impersonates the Vault endpoint, the attacker-controlled server can receive this token.
When AppRole authentication is used, the client sends:
role_id
secret_id
to:
/v1/auth/approle/login
A successful MITM can therefore capture both AppRole credentials.
The affected HCVault driver reads configuration including:
token.id
namespace
approle.role.id
approle.secret.id
as well as relevant VAULT_* environment variables.
However, affected versions do not expose configuration allowing operators to:
As a result, applications using the affected integration cannot restore TLS verification through the normal package configuration.
The package explicitly disables TLS server-certificate verification instead of relying on the secure default.
The vulnerable construction is effectively:
hvac.Client(..., verify=False)
rather than:
hvac.Client(..., verify=True)
or simply:
hvac.Client(...)
where verification is enabled by default.
Disabling certificate verification removes server authentication from the TLS connection.
Although the connection remains encrypted, the client has no reliable mechanism for determining whether it is communicating with the legitimate Vault server.
This creates the condition required for a network man-in-the-middle attack.
Successful exploitation requires:
The application uses an affected version of confluent-kafka:
2.8.0 through 2.14.2.The application uses Schema Registry encryption rules with the HCVault KMS integration.
The configured KMS key uses the:
hcvault://
scheme.
Possible examples include:
No application-level privileges or victim interaction are required once the necessary MITM position exists.
poc_confluent_kafka.py demonstrates the issue against a locally unpacked confluent-kafka 2.14.2 wheel.
The PoC uses:
HcVaultKmsClient implementation.The proof of concept contains five modes.
Install the PoC dependencies:
pip install hvac cryptography
Place the unpacked vulnerable wheel next to the PoC:
confluent_kafka-2.14.2-cp310-cp310-win_amd64/
└── confluent_kafka/
└── schema_registry/
└── ...
The PoC imports HcVaultKmsClient directly from the unpacked 2.14.2 package.
It stubs unrelated dependencies such as tink, allowing the vulnerable Vault-client construction to be tested without requiring a complete Kafka or KMS environment.
Use two terminals.
python poc_confluent_kafka.py server
The mock HTTPS Vault listens on:
https://localhost:8443
using a self-signed certificate.
First demonstrate that proper certificate validation rejects the self-signed server:
python poc_confluent_kafka.py control
Expected result:
EXPECTED TLS failure: CERTIFICATE_VERIFY_FAILED
-> verify=True correctly rejected the self-signed cert
Run:
python poc_confluent_kafka.py token
The vulnerable HcVaultKmsClient connects despite the server presenting an untrusted certificate.
The mock server can observe the Vault token:
GET /v1/auth/token/lookup-self
X-Vault-Token: CNA-DEMO-VAULT-TOKEN
X-Vault-Namespace: cna-demo-namespace
Run:
python poc_confluent_kafka.py approle
The mock Vault receives:
POST /v1/auth/approle/login
{"role_id": "CNA-DEMO-ROLE-ID", "secret_id": "CNA-DEMO-SECRET-ID"}
The contrast between the secure control mode and the vulnerable token / approle modes demonstrates that the behavior results from the hardcoded:
verify=False
rather than from the test environment.
The basic secure fix is to allow hvac to perform certificate verification:
hvac.Client(
url=vault_url,
token=token,
namespace=ns
)
Since TLS verification is enabled by default, explicitly disabling it is unnecessary.
A robust implementation should:
Enable TLS certificate verification by default.
Support a configurable CA bundle, for example:
ssl.ca.location
VAULT_CACERT
Support client certificates for environments using mutual TLS.
Prevent an empty or falsy configuration value from silently disabling certificate verification.
Add regression testing confirming that self-signed and otherwise untrusted certificates are rejected by default.
The vulnerability is fixed in:
confluent-kafka 2.15.0
Affected versions are:
2.8.0
through
2.14.2
In 2.15.0, the Vault client behavior was changed so that TLS verification defaults to enabled.
The updated implementation also introduces TLS-related configuration including:
ssl.ca.location
VAULT_CACERT
ssl.certificate.location
ssl.key.location
This allows deployments using private PKI infrastructure to provide trusted CA and client-certificate configuration without disabling certificate verification.
The vulnerability is scored:
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N
CVSS 3.1 Score: 7.4 — High
AV:N — Network
The vulnerable Vault communication occurs over a network connection.
AC:H — High Attack Complexity
The attacker must obtain a network MITM position or otherwise redirect the Vault connection.
This is a significant prerequisite and is why the vulnerability is rated High rather than Critical.
PR:N — No Privileges Required
The attacker does not require privileges within the affected application.
UI:N — No User Interaction
No user interaction is required after the attacker gains the necessary network position.
S:U — Scope Unchanged
The impact remains within the security authority of the affected application and its Vault interaction.
C:H — High Confidentiality Impact
Vault tokens or AppRole credentials may be exposed to the attacker.
I:H — High Integrity Impact
The attacker can impersonate Vault and return forged Vault/KMS responses.
A:N — No Availability Impact
No direct availability impact was demonstrated.
2.8.0 through 2.14.22.15.0The hardcoded verify=False existed from the introduction of the HCVault integration through version 2.14.2.
The security behavior was corrected in 2.15.0.
YYYY-MM-DD — Reported to Confluent.2.15.0.Discovered and reported by Rahul Karne.
confluent-kafka-python
https://github.com/confluentinc/confluent-kafka-python
confluent-kafka on PyPI
https://pypi.org/project/confluent-kafka/
HashiCorp hvac Python client
https://hvac.readthedocs.io/
HashiCorp Vault AppRole authentication
https://developer.hashicorp.com/vault/api-docs/auth/approle
CWE-295 — Improper Certificate Validation
https://cwe.mitre.org/data/definitions/295.html
Python TLS / certificate verification documentation
https://docs.python.org/3/library/ssl.html#ssl-security
This repository documents a coordinated-disclosure security finding and provides a harmless, self-contained proof of concept.
The PoC operates entirely against a local mock Vault server using dummy credentials.
It does not interact with real HashiCorp Vault, Kafka, Schema Registry, production infrastructure, or real credentials.
The material is provided for defensive security research and educational purposes.
Media inquiries: [email protected]. Full PoC (attacker server, traversal archive, victim application) and additional technical detail available on request.
| Mode | Description |
|---|
cert | Generates a self-signed certificate and key for the local mock Vault. |
server | Starts the mock HTTPS Vault on https://localhost:8443 and displays received requests. |
control | Secure control using verify=True; should reject the self-signed certificate with CERTIFICATE_VERIFY_FAILED. |
token | Loads the vulnerable HcVaultKmsClient and demonstrates that it accepts the untrusted certificate. |
approle | Exercises the vulnerable AppRole authentication path and demonstrates exposure of role_id and secret_id. |