Uma configuração de TLS correta é o que transforma o MQTT de um protocolo em texto puro em um protocolo que você pode usar com segurança pela internet. Neste guia você vai aprender o que o cliente realmente verifica durante o handshake, quais configurações importam e como configurar clientes Python, Node.js, ESP32 e de linha de comando para se conectar pela porta 8883. Todos os exemplos se conectam a um broker real, o servidor MQTT público (gratuito) da ConnectMQ, então você pode testar enquanto lê.
Por que o MQTT precisa de TLS
O MQTT em si não tem criptografia. Na porta 1883, o pacote CONNECT, incluindo usuário e senha, trafega como bytes legíveis, assim como todo payload de PUBLISH. Qualquer pessoa na mesma rede Wi-Fi de uma fábrica ou fazenda, um roteador comprometido ou um provedor curioso pode ler e até modificar esse tráfego. Para o contexto mais amplo de ameaças além da espionagem, leia nossa visão geral de melhores práticas de segurança MQTT.
O TLS (Transport Layer Security) envolve a conexão TCP em um túnel criptografado e autenticado antes que qualquer byte MQTT seja trocado. O MQTT não muda em nada; o broker simplesmente escuta em outra porta, por convenção a 8883, e o cliente faz primeiro um handshake TLS. Isso garante três propriedades:
- Confidencialidade: o tráfego é criptografado, então credenciais e payloads não podem ser lidos em trânsito.
- Integridade: pacotes adulterados são detectados e a conexão é encerrada.
- Autenticação do servidor: o cliente comprova que está falando com o broker verdadeiro, e não com um impostor.
A terceira propriedade é a que mais costuma ser quebrada por configurações descuidadas, e é o foco do restante deste artigo.
O que o cliente verifica durante o handshake
Quando seu cliente abre uma conexão TLS, o broker envia seu certificado e os eventuais certificados intermediários. O cliente então executa estas verificações:
- Cadeia de confiança: o certificado do servidor precisa ser assinado por um intermediário, que precisa ser assinado por uma CA raiz na qual o cliente já confia (seu repositório de CAs ou um arquivo de CA que você fornece).
- Período de validade: a hora atual precisa estar entre as datas "not before" e "not after" do certificado. É por isso que dispositivos sem relógio de tempo real precisam de NTP antes de se conectar.
- Correspondência do hostname: o hostname ao qual você se conectou precisa constar na lista Subject Alternative Name do certificado. Conectar-se por endereço IP a um certificado emitido para um hostname vai falhar, e deve mesmo.
- Posse da chave: o handshake comprova que o broker possui a chave privada correspondente ao certificado.
Dois detalhes relacionados costumam confundir. Primeiro, o SNI (Server Name Indication): o cliente envia o hostname dentro do handshake para que um servidor que hospeda vários nomes apresente o certificado certo. Bibliotecas modernas enviam o SNI automaticamente quando você informa um hostname. Segundo, em algumas bibliotecas a verificação do hostname é separada da verificação da cadeia. Um cliente pode aceitar um certificado perfeitamente válido para o domínio errado se a verificação de hostname estiver desativada, o que anula o propósito do TLS.
Configurações de TLS no MQTT em resumo
A tabela abaixo relaciona os transportes comuns com portas e esquemas de URL. As portas mostradas são as usadas pela ConnectMQ e pela maioria dos brokers que seguem a convenção.
| Transporte | Porta | Esquema de URL | Criptografado | Cliente típico |
|---|---|---|---|---|
| MQTT sobre TCP | 1883 | mqtt:// |
Não | Apenas testes de laboratório |
| MQTT sobre TLS | 8883 | mqtts:// |
Sim | Dispositivos, backends, CLI |
| MQTT sobre WebSocket | 8083 | ws:// |
Não | Testes locais no navegador |
| MQTT sobre WebSocket seguro | 8084 | wss:// (caminho /mqtt) |
Sim | Navegadores, redes restritivas |
Para a âncora de confiança, você tem três opções:
- Repositório de CAs do sistema: o padrão para Python, Node.js, Go e a maioria dos clientes de desktop ou servidor. Funciona quando o broker usa um certificado publicamente confiável.
- Arquivo de CA explícito: você informa um arquivo PEM com a CA raiz. Necessário para CAs privadas e para a maioria dos microcontroladores.
- Pinning: você confia apenas em uma CA ou chave específica. Mais forte, porém mais frágil; veja nosso guia de certificate pinning no ESP32.
O broker da ConnectMQ usa um certificado Let's Encrypt, que se encadeia à raiz ISRG Root X1, presente em todos os sistemas operacionais populares e nos pacotes de CAs do Python (via certifi) e do Node.js. Em desktops e servidores, normalmente você não precisa fornecer nenhum arquivo de CA.
Testando a conexão antes de escrever código
Antes de mexer no código da aplicação, confirme o caminho de rede e o certificado com o OpenSSL:
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -brief </dev/null
Procure por Verification: OK e pela versão de protocolo negociada. Se aparecer um erro de verificação aqui, nenhuma biblioteca de cliente terá sucesso; nosso artigo sobre como corrigir falhas no handshake TLS do MQTT percorre cada erro.
Depois, faça uma troca MQTT real com os clientes do mosquitto. Abra dois terminais e escolha um prefixo de topic único:
# Terminal 1: assina via TLS usando o diretório de CAs do sistema (Linux/macOS)
mosquitto_sub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-i tls-sub-7f3a -t "connectmq/demo/7f3a/#" -v
# Terminal 2: publica via TLS
mosquitto_pub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-i tls-pub-7f3a -t "connectmq/demo/7f3a/hello" -m "encrypted hello"
No Windows, o mosquitto não consegue ler o repositório do sistema, então baixe o PEM da ISRG Root X1 no site da Let's Encrypt e informe-o com --cafile isrgrootx1.pem. O servidor MQTT público aceita conexões anônimas, então esses comandos funcionam de imediato. Lembre-se de que a área pública pode ser lida por qualquer pessoa e serve apenas para testes e protótipos, então nunca envie dados reais de produção por ali.
Configuração do cliente Python (paho-mqtt 2.x)
Com o paho-mqtt 2.x, tls_set() sem argumentos carrega os certificados de CA padrão do sistema, ativa a verificação de hostname e exige um certificado válido. É exatamente o que você quer.
import ssl
import time
import paho.mqtt.client as mqtt
BROKER = "broker.connectmq.com"
PORT = 8883
TOPIC = "connectmq/demo/7f3a/python"
def on_connect(client, userdata, flags, reason_code, properties):
print("Connected:", reason_code)
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="py-tls-7f3a")
client.on_connect = on_connect
client.on_message = on_message
# Repositório de CAs do sistema, certificado obrigatório, hostname verificado,
# versão de TLS mais recente suportada pelos dois lados
client.tls_set(cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLS_CLIENT)
# Para um broker privado da ConnectMQ, adicione as credenciais do portal:
# client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.connect(BROKER, PORT, keepalive=60)
client.loop_start()
time.sleep(1)
client.publish(TOPIC, "hello over TLS", qos=1)
time.sleep(2)
client.loop_stop()
client.disconnect()
Se o repositório de CAs da sua plataforma estiver com problemas ou desatualizado, informe ca_certs=certifi.where(). Nunca "corrija" um erro com tls_insecure_set(True); isso desativa a verificação de hostname e remove silenciosamente a autenticação do servidor. Para os erros comuns em Python, veja SSL CERTIFICATE_VERIFY_FAILED no MQTT.
Configuração do cliente Node.js (MQTT.js v5)
O MQTT.js usa a pilha TLS nativa do Node, que valida contra a lista de CAs da Mozilla embutida no Node e verifica o hostname por padrão.
const mqtt = require("mqtt");
const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
clientId: "node-tls-7f3a",
protocolVersion: 5,
// Para um broker privado:
// username: "YOUR_USERNAME",
// password: "YOUR_PASSWORD",
rejectUnauthorized: true, // padrão; nunca defina como false em produção
});
const topic = "connectmq/demo/7f3a/node";
client.on("connect", () => {
console.log("Connected over TLS");
client.subscribe(topic, { qos: 1 }, () => {
client.publish(topic, "hello from Node.js", { qos: 1 });
});
});
client.on("message", (t, payload) => {
console.log(`${t}: ${payload.toString()}`);
client.end();
});
client.on("error", (err) => console.error("MQTT error:", err.message));
Se precisar confiar em uma CA privada, adicione ca: fs.readFileSync("ca.pem") às opções. No navegador, use wss://broker.connectmq.com:8084/mqtt; o próprio navegador cuida da validação do certificado.
Configuração do cliente ESP32
Microcontroladores não têm um repositório de CAs de sistema operacional, então você embute o certificado raiz. Com o core Arduino, WiFiClientSecure mais PubSubClient fica assim:
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <time.h>
const char* WIFI_SSID = "your-ssid";
const char* WIFI_PASS = "your-password";
const char* BROKER = "broker.connectmq.com";
// Cole aqui o PEM completo da ISRG Root X1, obtido em letsencrypt.org
static const char ROOT_CA[] = R"EOF(
-----BEGIN CERTIFICATE-----
...ISRG Root X1 certificate body...
-----END CERTIFICATE-----
)EOF";
WiFiClientSecure net;
PubSubClient mqtt(net);
void setup() {
Serial.begin(115200);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) delay(250);
// Um relógio válido é necessário para checar as datas do certificado
configTime(0, 0, "pool.ntp.org");
while (time(nullptr) < 1700000000) delay(250);
net.setCACert(ROOT_CA);
mqtt.setServer(BROKER, 8883);
}
void loop() {
if (!mqtt.connected()) {
if (mqtt.connect("esp32-tls-7f3a")) {
mqtt.publish("connectmq/demo/7f3a/esp32", "hello over TLS");
} else {
Serial.printf("MQTT connect failed, state=%d\n", mqtt.state());
delay(5000);
}
}
mqtt.loop();
}
Para um broker privado, use mqtt.connect("YOUR_CLIENT_ID", "YOUR_USERNAME", "YOUR_PASSWORD"). O tutorial de MQTT sobre SSL no ESP32 aborda com mais profundidade o uso de memória e o comportamento de reconexão.
Checklist para produção
- Use a porta 8883 (ou a 8084 para navegadores) em todo lugar fora do laboratório; desative ou bloqueie no firewall a 1883 sem criptografia para clientes de produção.
- Verifique a cadeia de certificados contra uma raiz confiável, e não "aceite qualquer coisa".
- Mantenha a verificação de hostname ativada e sempre se conecte pelo hostname, nunca pelo IP.
- Sincronize o horário (NTP) nos dispositivos embarcados antes do handshake.
- Confie na CA raiz, não no certificado folha, porque certificados folha são renovados com frequência.
- Envie credenciais apenas via TLS e guarde-as fora do código-fonte.
- Prepare-se para mudanças de CA: torne a raiz confiável atualizável over-the-air.
- Monitore conexões com falha; falhas repetidas de TLS ou de autenticação geralmente indicam erro de configuração ou ataque.
Deixe um broker MQTT gerenciado cuidar do lado do servidor
O lado do broker no TLS é a metade menos divertida: obter certificados, renová-los antes que expirem, escolher versões de protocolo e cifras e reiniciar listeners sem derrubar clientes. Um broker MQTT gerenciado elimina esse trabalho. A ConnectMQ termina o TLS na 8883 e o WebSocket seguro na 8084 com certificados Let's Encrypt renovados automaticamente, então seus clientes só precisam da configuração padrão mostrada acima. Se estiver comparando opções de hospedagem, nosso guia de broker MQTT na nuvem explica os prós e contras.
Comece a usar a ConnectMQ
Crie uma conta gratuita em app.connectmq.com, escolha o plano Serverless (Free) sem cartão de crédito, crie um broker e copie o usuário, a senha e o client ID gerados. Aponte qualquer um dos clientes acima para a porta 8883 com essas credenciais e você terá uma conexão MQTT criptografada e autenticada em minutos.
Perguntas frequentes
MQTT sobre TLS é mais lento que MQTT sem criptografia?
O handshake acrescenta idas e voltas e trabalho de CPU quando uma conexão é aberta, mas as conexões MQTT são duradouras, então a sobrecarga em regime contínuo é pequena. Mantenha as conexões abertas e reutilize sessões em vez de se reconectar a cada mensagem.
Preciso de um certificado de cliente para usar MQTT sobre TLS?
Não. A maioria das implantações usa TLS no lado do servidor mais usuário e senha enviados dentro do túnel criptografado. Certificados de cliente (TLS mútuo) são uma opção adicional que alguns brokers auto-hospedados suportam.
De qual certificado de CA preciso para a ConnectMQ?
O certificado do broker é emitido pela Let's Encrypt e se encadeia à ISRG Root X1. Clientes de desktop e servidor já confiam nela; dispositivos embarcados devem embutir o PEM dessa raiz.
Por que meu cliente falha quando me conecto pelo endereço IP?
O certificado lista hostnames, e não endereços IP, então a verificação de hostname falha. Conecte-se sempre usando broker.connectmq.com ou o hostname do seu broker.



