Segurança MQTT

Erro SSL Certificate Verify Failed no MQTT: Como Corrigir Clientes Python

Recebendo "[SSL: CERTIFICATE_VERIFY_FAILED]" quando seu cliente MQTT em Python se conecta na porta 8883? Veja como descobrir a causa exata e corrigi-la do jeito certo em qualquer plataforma, sem desativar a verificação.

Ilustração do erro SSL certificate verify failed no MQTT com um terminal Python mostrando uma cadeia de certificados rejeitada na porta 8883
Rode isso no seu próprio broker MQTTTenha um broker MQTT privado e gerenciado no plano Free e comece com exemplos de código prontos.
Teste grátis Exemplos de código Sem cartão de crédito

O erro MQTT "ssl certificate verify failed" significa que o Python recusou o certificado do broker durante o handshake TLS. É um recurso de segurança fazendo exatamente o seu trabalho, e a solução quase nunca é desligá-lo. Este guia mostra como ler o motivo exato na mensagem de erro e corrigi-lo corretamente com o paho-mqtt no Windows, macOS, Linux e Docker, usando broker.connectmq.com na porta 8883 como referência que sabidamente funciona.

Lendo a mensagem de erro

A exceção completa normalmente tem esta cara:

ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed:
unable to get local issuer certificate (_ssl.c:1000)

O que importa é a parte depois dos dois-pontos. As variantes mais comuns apontam para causas diferentes:

Detalhe da mensagem Significado Correção mais provável
unable to get local issuer certificate O Python não encontra a CA raiz que assinou a cadeia Corrigir ou fornecer o bundle de CAs
self-signed certificate in certificate chain Algo apresentou um certificado assinado por uma CA privada não confiável Inspeção por proxy ou antivírus, ou uma CA privada do broker que você não configurou
Hostname mismatch, certificate is not valid for '...' O nome ao qual você se conectou não consta no certificado Conectar usando o hostname correto, não um IP
certificate has expired O relógio está errado ou o certificado do servidor expirou Acertar a hora do sistema ou renovar o certificado do servidor

Passo 1: confirme que o Python consegue verificar o broker

Este script curto testa o TLS sem MQTT, usando exatamente as configurações que o paho-mqtt usa por padrão:

import socket
import ssl

HOST, PORT = "broker.connectmq.com", 8883

ctx = ssl.create_default_context()
print("OpenSSL:", ssl.OPENSSL_VERSION)
print("Default verify paths:", ssl.get_default_verify_paths())

with socket.create_connection((HOST, PORT), timeout=10) as sock:
    with ctx.wrap_socket(sock, server_hostname=HOST) as tls:
        cert = tls.getpeercert()
        print("Connected with", tls.version())
        print("Issuer:", dict(x[0] for x in cert["issuer"]))
        print("Valid until:", cert["notAfter"])

Se este script falhar com o mesmo erro, o problema está no repositório de confiança do ambiente Python, no relógio ou na rede. Se ele funcionar, mas o seu código MQTT falhar, o problema está na configuração do cliente MQTT: um caminho de ca_certs errado, conexão por IP ou um broker com certificado privado.

Passo 2: use uma configuração TLS correta no paho-mqtt

Com o paho-mqtt 2.x, esta é a base que funciona com qualquer broker que tenha um certificado publicamente confiável, incluindo o ConnectMQ:

import ssl
import time
import certifi
import paho.mqtt.client as mqtt

BROKER = "broker.connectmq.com"
TOPIC = "connectmq/demo/a8d2/verify-test"

def on_connect(client, userdata, flags, reason_code, properties):
    print("CONNACK:", reason_code)
    client.subscribe(TOPIC)
    client.publish(TOPIC, "certificate verified")

def on_message(client, userdata, msg):
    print("Received:", msg.payload.decode())

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="py-verify-a8d2")
client.on_connect = on_connect
client.on_message = on_message

# o certifi traz o bundle de CAs da Mozilla, independente do repositório do SO
client.tls_set(ca_certs=certifi.where(), cert_reqs=ssl.CERT_REQUIRED)

# Broker privado ConnectMQ:
# client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")

client.connect(BROKER, 8883, keepalive=60)
client.loop_start()
time.sleep(3)
client.loop_stop()
client.disconnect()

Instale as dependências com pip install "paho-mqtt>=2.0" certifi. Passar certifi.where() deixa o comportamento idêntico em todos os sistemas operacionais, o que muitas vezes é a correção definitiva mais rápida. Mantenha o certifi atualizado, porque as listas de CAs mudam com o tempo.

Correções para cada plataforma

macOS

Os instaladores do Python baixados de python.org incluem o próprio OpenSSL e não usam o Keychain do macOS. Até você executar o script que vem junto, eles simplesmente não têm nenhum certificado de CA, o que gera unable to get local issuer certificate. Resolva executando o script de certificados do instalador:

/Applications/Python\ 3.12/Install\ Certificates.command

Ajuste o número da versão de acordo com a sua instalação. O Python do Homebrew usa o bundle de CAs do Homebrew e normalmente funciona de primeira; se não funcionar, execute brew reinstall ca-certificates. Usar certifi.where() explicitamente evita o problema em qualquer caso.

Windows

No Windows, o Python carrega os certificados do repositório de certificados do Windows. As falhas costumam ter uma de duas causas:

  • Um repositório de raízes desatualizado em uma máquina que não recebeu atualizações, de modo que falta uma raiz mais nova. Instale as atualizações do Windows ou use o certifi.
  • Inspeção TLS feita por antivírus ou proxy corporativo, que gera self-signed certificate in certificate chain. Confira o emissor exibido pelo script do Passo 1; se ele mencionar um produto de segurança, é esse produto que está interceptando a conexão.

Linux

O Python usa os caminhos padrão do OpenSSL, geralmente /etc/ssl/certs/ca-certificates.crt no Debian e Ubuntu ou /etc/pki/tls/certs/ca-bundle.crt em sistemas baseados em Red Hat. Se o pacote estiver ausente ou desatualizado:

# Debian / Ubuntu / Raspberry Pi OS
sudo apt update && sudo apt install --reinstall ca-certificates
sudo update-ca-certificates

# Fedora / RHEL
sudo dnf reinstall ca-certificates

Docker e imagens slim

Imagens como python:3.12-slim normalmente incluem certificados de CA, mas as variantes alpine e distroless podem não incluir. Adicione o pacote explicitamente:

FROM python:3.12-alpine
RUN apk add --no-cache ca-certificates

Ou instale o certifi na imagem e faça referência a ele no código. Você também pode apontar o OpenSSL para um bundle com a variável de ambiente SSL_CERT_FILE.

A variante hostname mismatch

Hostname mismatch aparece quando você chama client.connect("203.0.113.10", 8883) ou usa um alias interno que não consta no certificado. O TLS verifica se o nome solicitado aparece nos Subject Alternative Names do certificado. Conecte-se usando o hostname real do broker. No ConnectMQ, ele é broker.connectmq.com. Se precisar forçar um IP específico para testes, coloque-o no arquivo hosts em vez de se conectar pelo IP.

Brokers privados ou autoassinados

Se você roda seu próprio broker com uma CA privada, o bundle de CAs públicas nunca vai confiar nele. Passe a CA privada explicitamente com client.tls_set(ca_certs="my-ca.crt") e garanta que o certificado do broker inclua o hostname correto no SAN. O processo e o trabalho de manutenção que ele exige estão explicados em certificados autoassinados para MQTT.

Inspecionando a cadeia com o OpenSSL

Quando a saída do Python não basta, o OpenSSL mostra a cadeia exata que o servidor envia e onde a verificação para:

openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -showcerts </dev/null

Leia a seção Certificate chain de cima para baixo: a entrada 0 é o certificado do broker, as seguintes são intermediárias, e cada linha i: (emissor) deve corresponder à linha s: (sujeito) da próxima entrada. O Verify return code final informa se o repositório de confiança do próprio OpenSSL aceita a cadeia. Se o OpenSSL disser 0 (ok) mas o Python falhar, o Python está usando um repositório de confiança diferente do OpenSSL; imprimir ssl.get_default_verify_paths(), como no Passo 1, geralmente revela isso. Se o emissor da entrada 0 mencionar o fabricante do seu antivírus ou firewall, você está vendo uma inspeção TLS, e não o certificado real do broker.

Para comparar especificamente com o certifi, adicione -CAfile com o caminho exibido por python -c "import certifi; print(certifi.where())". Sucesso com o certifi e falha sem ele confirmam que o problema está no repositório do sistema operacional.

O que não fazer

Resultados de busca costumam sugerir estas linhas:

client.tls_set(cert_reqs=ssl.CERT_NONE)
client.tls_insecure_set(True)

Elas fazem o erro sumir desativando a autenticação do servidor. Suas credenciais passam a ir para qualquer servidor que responder, inclusive o de um atacante. Use-as, se for usar, apenas por alguns minutos de depuração local contra um broker de laboratório, nunca em código que vai para o repositório. O artigo de boas práticas de segurança MQTT explica por que autenticar o broker importa tanto quanto a criptografia.

Checklist de correção rápida

  1. Leia o detalhe depois de certificate verify failed.
  2. Execute o script do Passo 1 no mesmo ambiente.
  3. Use certifi.where() como ca_certs ou repare o repositório do SO.
  4. Conecte-se pelo hostname, não pelo IP.
  5. Verifique date -u para descartar problemas de relógio.
  6. Olhe o emissor do certificado para detectar proxies.
  7. Para brokers privados, forneça seu próprio arquivo de CA.

Se continuar travado depois de resolver o handshake, nosso guia sobre como corrigir falhas de handshake TLS no MQTT cobre portas, SNI e versões de protocolo, e o tutorial de MQTT com TLS em Python mostra padrões completos para produção.

Por que certificados públicos economizam tempo

A maioria das falhas de verificação de certificado desaparece quando o broker usa um certificado de uma CA publicamente confiável e o renova automaticamente. É assim que o ConnectMQ funciona: um broker MQTT gerenciado com certificado Let's Encrypt nas portas 8883 e 8084, de modo que instalações padrão do Python o verificam sem arquivos extras. Você pode reproduzir todos os testes deste artigo no servidor MQTT público (gratuito), feito para testes e não para dados de produção, antes de passar para credenciais privadas.

Comece a usar o ConnectMQ

Cadastre-se em app.connectmq.com, escolha o plano Serverless gratuito, sem cartão de crédito, crie um broker e execute o exemplo de paho-mqtt acima com o username, a senha e o client ID gerados para você.

Perguntas frequentes

Por que o mesmo código funciona no Linux, mas falha no macOS?

A versão do Python para macOS de python.org vem sem certificados de CA até você executar o "Install Certificates.command". O Python no Linux usa o bundle do sistema, que normalmente já está instalado.

É seguro usar o certifi em vez do repositório do sistema operacional?

Sim. O certifi empacota a lista de raízes curada pela Mozilla. Basta mantê-lo atualizado com o pip, lembrando que ele não inclui CAs privadas ou corporativas.

Esse erro significa que o broker foi comprometido?

Raramente. Bundles de CA ausentes, hostnames errados, problemas de relógio e inspeção TLS são muito mais comuns. Verifique o nome do emissor para ter certeza.

Preciso do arquivo de certificado do ConnectMQ?

Não. O ConnectMQ usa um certificado Let's Encrypt publicamente confiável, então um bundle de CAs padrão, como o certifi ou o repositório do SO, é suficiente.

Artigos relacionados

Listas de Controle de Acesso (ACLs) no MQTT: Conceitos e Design

O que são as listas de controle de acesso MQTT, como brokers como o Mosquitto expressam regras de leitura e escrita por topic, como se comportam as operações negadas e como projetar namespaces de topics para manter a autorização simples.

8 min de leitura

Todos os artigos →

Seu broker MQTT privado está a poucos cliques de distância

Crie uma conta, receba as credenciais do seu broker e conecte seu primeiro dispositivo — sem servidores para instalar, atualizar ou escalar.