TLS com MQTT em Python é a diferença entre mandar o seu usuário, a sua senha e os dados dos seus sensores pela internet em texto puro e mandá-los criptografados e autenticados. Neste tutorial você vai configurar o paho-mqtt para conectar na porta 8883 usando os certificados CA confiáveis do seu sistema operacional, manter a verificação de hostname ativada e corrigir os erros de certificado que travam a maioria das pessoas. Todos os exemplos se conectam ao broker.connectmq.com, o servidor MQTT público da ConnectMQ para testes, que serve um certificado Let's Encrypt publicamente confiável, então nenhum arquivo de CA personalizado é necessário.
O que o TLS faz pelo MQTT
O MQTT em si não tem criptografia. Na porta 1883 tudo, inclusive o pacote CONNECT com as suas credenciais, trafega como bytes legíveis. O TLS envolve a conexão TCP e oferece três garantias:
- Confidencialidade: ninguém no caminho da rede consegue ler payloads ou senhas.
- Integridade: ninguém consegue alterar mensagens em trânsito sem ser detectado.
- Autenticação do servidor: o seu cliente verifica que está realmente falando com o broker que pediu, e não com um impostor.
O terceiro ponto é o que as pessoas desativam sem querer. Criptografia sem verificação protege contra bisbilhoteiros passivos, mas não contra um atacante ativo que apresenta o próprio certificado. O guia de boas práticas de segurança MQTT cobre o panorama completo; este artigo se concentra em acertar o lado do cliente.
O ConnectMQ expõe estas portas em todos os brokers:
| Porta | Transporte | Criptografada | Uso a partir do Python |
|---|---|---|---|
| 1883 | MQTT sobre TCP | Não | Apenas testes locais |
| 8883 | MQTT sobre TLS | Sim | Padrão para produção |
| 8083 | MQTT sobre WebSocket | Não | Raramente necessária no Python |
| 8084 | MQTT sobre WebSocket seguro, caminho /mqtt |
Sim | Quando só portas web são permitidas |
Pré-requisitos
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install "paho-mqtt>=2.0"
Antes de escrever qualquer linha de Python, verifique se o certificado do broker é validado a partir da sua máquina. Este comando mostra a cadeia e o resultado da verificação:
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -brief </dev/null
Procure por Verification: OK. Se o OpenSSL já falhar aqui, o problema está no repositório de CAs do seu sistema ou na rede, não no seu código.
O cliente mínimo de MQTT com TLS em Python
Salve isto como tls_client.py e troque k7f3q2 por uma string aleatória sua:
import ssl
import time
import uuid
import paho.mqtt.client as mqtt
BROKER = "broker.connectmq.com"
PORT = 8883
TOPIC = "connectmq/demo/k7f3q2/secure"
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"Connect failed: {reason_code}")
return
sock = client.socket()
print(f"Connected over {sock.version()} using {sock.cipher()[0]}")
client.subscribe(TOPIC, qos=1)
def on_message(client, userdata, msg):
print(f"{msg.topic}: {msg.payload.decode()}")
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"tls-{uuid.uuid4().hex[:8]}")
client.on_connect = on_connect
client.on_message = on_message
# Usa as CAs confiáveis do sistema operacional, exige um certificado válido
# e verifica se ele corresponde a BROKER. Este é o padrão seguro.
client.tls_set(cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLS_CLIENT)
client.connect(BROKER, PORT, keepalive=60)
client.loop_start()
time.sleep(2)
client.publish(TOPIC, "hello over TLS", qos=1)
time.sleep(2)
client.disconnect()
client.loop_stop()
Execute e você deverá ver algo como Connected over TLSv1.3 using TLS_AES_256_GCM_SHA384, seguido da sua mensagem voltando.
O que o tls_set() faz
Chamado sem o argumento ca_certs, o tls_set() monta um contexto SSL que carrega os certificados confiáveis padrão da plataforma, exige que o servidor apresente um certificado válido e confere se o nome do certificado corresponde ao host passado para connect(). Os argumentos explícitos cert_reqs e tls_version acima são os valores padrão, escritos para deixar a intenção óbvia para quem ler o código depois.
Se preferir controle total, monte o contexto você mesmo e entregue-o ao cliente:
context = ssl.create_default_context()
context.minimum_version = ssl.TLSVersion.TLSv1_2
client.tls_set_context(context)
ssl.create_default_context() ativa a checagem de hostname e a verificação de certificado, e definir uma versão mínima rejeita protocolos legados. Use tls_set() ou tls_set_context(), não os dois.
Adicionando credenciais para um broker privado
O TLS protege as credenciais que você envia, então sempre combine os dois. Com um broker criado no portal do ConnectMQ:
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="YOUR_CLIENT_ID")
client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.tls_set()
client.connect("broker.connectmq.com", 8883, keepalive=60)
Use o nome de host e o client ID exibidos no portal; o broker vincula os client IDs às credenciais que emite. Carregue os segredos de variáveis de ambiente (os.environ["MQTT_PASSWORD"]) em vez de enviá-los ao controle de versão.
WebSocket seguro a partir do Python
Algumas redes bloqueiam tudo, exceto as portas web padrão. O paho consegue tunelar o MQTT por WebSocket seguro na porta 8084, com a mesma verificação TLS:
client = mqtt.Client(
mqtt.CallbackAPIVersion.VERSION2,
client_id=f"wss-{uuid.uuid4().hex[:8]}",
transport="websockets",
)
client.ws_set_options(path="/mqtt")
client.tls_set()
client.connect("broker.connectmq.com", 8084, keepalive=60)
Todo o resto, callbacks, assinaturas e publicação, continua igual. O artigo sobre MQTT sobre WebSocket explica quando esse transporte faz sentido.
Corrigindo erros comuns de TLS
A maioria das falhas de TLS se resume a poucas causas. Associe a sua mensagem de erro a esta tabela:
| Erro | Causa provável | Correção |
|---|---|---|
CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate |
O Python não encontra as CAs confiáveis | Veja as correções do repositório de CAs abaixo |
Hostname mismatch, certificate is not valid for '...' |
Você conectou por endereço IP ou por um alias | Conecte com o nome de host exato do broker |
certificate is not yet valid ou certificate has expired |
O relógio do dispositivo está errado | Sincronize a hora com NTP antes de conectar |
WRONG_VERSION_NUMBER |
Cliente TLS falando com a porta 1883 sem criptografia | Use a porta 8883 |
| A conexão trava e depois estoura o timeout | Cliente sem TLS falando com a porta TLS 8883, ou porta bloqueada | Chame tls_set() ou tente a porta 8084 |
Corrigindo um repositório de CAs ausente
- macOS com Python do python.org: esse Python vem sem acesso ao keychain do sistema. Execute o script "Install Certificates.command" na pasta do aplicativo Python ou aponte o paho para o certifi:
pip install certifie depoisclient.tls_set(ca_certs=certifi.where()). - Imagens Docker enxutas: instale o pacote de CAs, por exemplo
apt-get install -y ca-certificatesem imagens baseadas em Debian. - Redes corporativas com inspeção TLS: um proxy pode substituir os certificados pelos dele. Peça à TI a CA raiz corporativa e passe-a com
ca_certs=em vez de desativar a verificação. - Linux embarcado: atualize o pacote de CAs; imagens muito antigas podem não incluir o certificado ISRG Root X1, ao qual a cadeia do Let's Encrypt se liga.
Nunca "corrija" o TLS desligando a verificação
Você vai encontrar conselhos para chamar client.tls_insecure_set(True) ou passar cert_reqs=ssl.CERT_NONE. Os dois fazem o erro sumir aceitando qualquer certificado de qualquer um, o que remove silenciosamente a autenticação do servidor. Use-os apenas para um diagnóstico rápido na sua própria rede de laboratório, nunca em código implantado.
Evite também fixar (pinning) o certificado folha do broker no cliente. Os certificados Let's Encrypt são renovados regularmente, então um certificado fixado vai quebrar na próxima renovação. Confie na CA, que é o que o repositório padrão faz.
Checklist de produção
Antes de implantar um cliente Python que usa TLS, confirme cada item:
- A porta 8883 (ou a 8084 com WebSocket) é usada em todo lugar; nada recorre à 1883.
tls_set()outls_set_context()é chamado semtls_insecure_set(True).- O cliente conecta pelo nome de host, nunca por endereço IP.
- O dispositivo mantém a hora correta, sincronizada por NTP na inicialização.
- O pacote de CAs do dispositivo é mantido e atualizado junto com o sistema operacional.
- As credenciais vêm de variáveis de ambiente ou de um cofre de segredos.
- Há lógica de reconexão; o guia de reconexão do paho-mqtt mostra um padrão robusto.
Próximos passos
Se você é novo no paho, o tutorial de Paho MQTT em Python cobre o básico de publish e subscribe, e o guia de aiomqtt e asyncio mostra como passar um tls_context para um cliente assíncrono. Para clientes em outras linguagens, a visão geral de bibliotecas de cliente MQTT compara o suporte a TLS entre ecossistemas. A documentação do módulo ssl do Python é a referência de cada opção de contexto usada aqui.
Proteja o seu tráfego MQTT com o ConnectMQ
O ConnectMQ é um broker MQTT gerenciado com TLS na 8883 e WebSocket seguro na 8084 ativados desde o início, com um certificado Let's Encrypt gerenciado automaticamente, para que os seus clientes Python verifiquem o broker com o repositório de CAs que já têm. Teste no servidor MQTT público e depois migre para um broker MQTT na nuvem privado com credenciais geradas. Crie o seu broker gratuito no plano Serverless, sem cartão de crédito.
Perguntas frequentes
Preciso baixar um certificado CA para conectar com TLS?
Não. O broker usa um certificado Let's Encrypt cuja cadeia leva a uma raiz confiável nos sistemas operacionais atuais, então tls_set() com o repositório de CAs padrão funciona.
Qual porta o MQTT sobre TLS usa?
A porta padrão é a 8883. O ConnectMQ também oferece MQTT sobre WebSocket seguro na porta 8084, com o caminho /mqtt.
Por que o python mqtt tls falha quando conecto por endereço IP?
A verificação de hostname compara o nome do certificado com o host ao qual você se conectou. Um endereço IP não corresponde ao nome do broker, então a checagem falha por design; use o nome de host.
O TLS basta para proteger o meu cliente MQTT?
O TLS protege a conexão, mas você ainda precisa de credenciais fortes por dispositivo e de um design de topics cuidadoso. Combine o TLS com credenciais exclusivas geradas no portal do broker.



