O MQTT com TLS 1.3 encurta o handshake, elimina criptografia fraca e simplifica a escolha de cipher suites, mas em dispositivos alimentados por bateria os custos reais vêm de outro lugar. Este artigo explica o que realmente muda entre o TLS 1.2 e o 1.3 nas conexões MQTT, quais cipher suites permitir, como medir o que a sua conexão negocia e como configurar clientes Python, Node.js e Mosquitto de forma sensata.
Por que a versão do TLS importa para o MQTT
O MQTT roda sobre uma conexão TCP de longa duração. Um dispositivo se conecta uma vez, faz um handshake TLS e depois troca mensagens pequenas por horas ou dias. Isso significa que o custo do handshake é pago raramente por dispositivos sempre ligados, mas com muita frequência por dispositivos que dormem e se reconectam, como sensores a bateria que acordam a cada poucos minutos. Para esses dispositivos, os round trips do handshake e o tempo de CPU gasto em operações de chave pública dominam o consumo de energia, e a versão do TLS e a escolha da cipher suite fazem muita diferença.
Se você está começando a proteger conexões MQTT, comece pelo nosso guia de configuração de TLS no MQTT e volte aqui para os detalhes de ajuste fino.
TLS 1.2 vs TLS 1.3 em resumo
| Aspecto | TLS 1.2 | TLS 1.3 |
|---|---|---|
| Round trips do handshake completo | 2 antes dos dados da aplicação | 1 antes dos dados da aplicação |
| Handshake retomado | 1 round trip (session ID ou ticket) | 1 round trip (PSK), 0-RTT opcional |
| Troca de chaves | RSA ou (EC)DHE | Apenas (EC)DHE, sempre com forward secrecy |
| Cipher suites | Dezenas, muitas fracas ou obsoletas | Cinco definidas, três amplamente usadas |
| Certificado criptografado no handshake | Não | Sim |
| Modo CBC, RC4, SHA-1, RSA estático | Permitidos (conforme a configuração) | Removidos |
Para um cliente em um link celular de alta latência, economizar um round trip a cada conexão é perceptível. Para um dispositivo em rede local, a economia é pequena comparada ao trabalho criptográfico.
De onde vem de fato o custo do handshake
Em um microcontrolador, três operações dominam um handshake completo:
- Troca de chaves: um cálculo ECDHE, normalmente na curva X25519 ou P-256. Isso acontece nas duas versões do TLS.
- Verificação do certificado: checar as assinaturas da cadeia do servidor. Verificar assinaturas RSA é barato; a verificação ECDSA custa mais no cliente, mas certificados ECDSA são menores.
- Round trips de rede: cada um mantém o rádio acordado.
O TLS 1.3 reduz os round trips, mas não elimina as operações de chave pública. O maior ganho para dispositivos que se reconectam com frequência costuma ser a retomada de sessão, que pula a verificação do certificado e, no modo PSK com troca de chaves do TLS 1.3, ainda oferece forward secrecy. Verifique se a sua pilha TLS e o broker suportam esse recurso antes de depender dele.
Aceleração por hardware também conta. A família ESP32 acelera AES, SHA e a aritmética de números grandes usada em RSA e ECC por meio do mbedTLS, o que torna qualquer uma das versões do TLS viável nela. Microcontroladores menores, sem aceleração, podem se beneficiar do ChaCha20-Poly1305, que é rápido em software puro.
Cipher suites do MQTT com TLS 1.3
O TLS 1.3 separa a cipher suite (criptografia simétrica e hash) dos algoritmos de troca de chaves e de assinatura. As suites que você vai encontrar na prática:
TLS_AES_128_GCM_SHA256: de implementação obrigatória, rápida com hardware AES, um bom padrão.TLS_AES_256_GCM_SHA384: chave maior, um pouco mais lenta, muitas vezes escolhida por questões de política.TLS_CHACHA20_POLY1305_SHA256: excelente em CPUs sem aceleração AES.
As três são criptografia autenticada e consideradas fortes. Raramente é preciso restringi-las. As duas suites restantes definidas para o TLS 1.3 (variantes CCM) aparecem principalmente em ambientes com recursos limitados.
Para o TLS 1.2, se ainda precisar suportá-lo, permita apenas suites AEAD com forward secrecy:
ECDHE-ECDSA-AES128-GCM-SHA256
ECDHE-RSA-AES128-GCM-SHA256
ECDHE-ECDSA-CHACHA20-POLY1305
ECDHE-RSA-CHACHA20-POLY1305
ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-RSA-AES256-GCM-SHA384
Evite suites com CBC, RC4, 3DES, NULL, EXPORT ou as que usam troca de chaves RSA estática (nomes que começam com AES sem ECDHE).
Medindo o que a sua conexão negocia
Nunca presuma qual versão um broker suporta; meça. O OpenSSL consegue forçar cada versão e informar o resultado:
# Negocia a melhor versão suportada pelos dois lados
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -brief </dev/null
# Força TLS 1.3
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -tls1_3 -brief </dev/null
# Força TLS 1.2
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -tls1_2 -brief </dev/null
As linhas Protocol version e Ciphersuite mostram o resultado. Execute os mesmos testes em qualquer broker que estiver avaliando e repita-os após atualizações do broker ou das bibliotecas, porque as versões suportadas podem mudar com o tempo.
Para cronometrar handshakes a partir de um script, este trecho em Python mede a latência de conexão mais handshake:
import socket
import ssl
import time
HOST, PORT = "broker.connectmq.com", 8883
def handshake(version: ssl.TLSVersion) -> None:
ctx = ssl.create_default_context()
ctx.minimum_version = version
ctx.maximum_version = version
start = time.perf_counter()
try:
with socket.create_connection((HOST, PORT), timeout=10) as sock:
with ctx.wrap_socket(sock, server_hostname=HOST) as tls:
elapsed = (time.perf_counter() - start) * 1000
print(f"{tls.version()} {tls.cipher()[0]} {elapsed:.1f} ms")
except ssl.SSLError as exc:
print(f"{version.name} not negotiated: {exc.reason}")
for v in (ssl.TLSVersion.TLSv1_2, ssl.TLSVersion.TLSv1_3):
for _ in range(3):
handshake(v)
Execute várias vezes; em um link rápido, a variação da rede costuma ser maior que a diferença entre as versões.
Configurando os clientes
A política de cliente mais segura é "permitir TLS 1.2 ou superior, preferindo a mais nova", o que deixa cliente e servidor escolherem a melhor versão em comum.
Python (paho-mqtt 2.x)
import ssl
import paho.mqtt.client as mqtt
ctx = ssl.create_default_context() # verifica a cadeia e o hostname
ctx.minimum_version = ssl.TLSVersion.TLSv1_2 # recusa qualquer versão mais antiga
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="py-tls13-c71e")
client.tls_set_context(ctx)
# client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.connect("broker.connectmq.com", 8883)
client.loop_start()
client.publish("connectmq/demo/c71e/tls", "negotiated securely", qos=1).wait_for_publish()
client.loop_stop()
client.disconnect()
O set_ciphers() do Python afeta apenas o TLS 1.2 e anteriores; as suites do TLS 1.3 são gerenciadas pelos padrões do OpenSSL, que já são adequados.
Node.js (MQTT.js v5)
const mqtt = require("mqtt");
const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
clientId: "node-tls13-c71e",
protocolVersion: 5,
minVersion: "TLSv1.2", // repassado para o tls.connect do Node
});
client.on("connect", () => {
const sock = client.stream;
console.log("TLS:", sock.getProtocol(), sock.getCipher().name);
client.end();
});
Mosquitto como broker auto-hospedado
Se você roda seu próprio broker Mosquitto, você controla o lado do servidor:
listener 8883
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key
cafile /etc/mosquitto/certs/chain.pem
tls_version tlsv1.2
ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305
ciphers_tls1.3 TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384
No Mosquitto, tls_version define a versão mínima aceita no listener. Restringir um servidor apenas ao TLS 1.3 é tentador, mas bloqueia firmwares mais antigos e pilhas TLS embarcadas que ainda suportam somente o TLS 1.2.
Dispositivos com recursos limitados: conselhos práticos
- Mantenha as conexões abertas. Um handshake por dia sai mais barato que qualquer otimização de um handshake por minuto.
- Use retomada de sessão onde a sua pilha suportar, para dispositivos que precisam dormir.
- Ative a criptografia por hardware na configuração do seu SDK.
- Verifique o status do TLS 1.3 na sua pilha. Versões recentes do mbedTLS usadas pelo ESP-IDF podem ser compiladas com suporte a TLS 1.3 como opção de configuração; SDKs mais antigos oferecem apenas TLS 1.2, que continua seguro com as suites listadas acima.
- Não fixe uma única versão no firmware. Permita a negociação para que os dispositivos continuem funcionando quando os servidores mudarem.
- Evite 0-RTT para comandos. Early data pode ser reenviado (replay), o que é perigoso para comandos de atuadores.
O guia de certificate pinning no ESP32 cobre o lado dos certificados no TLS embarcado, e como corrigir falhas de handshake TLS no MQTT ajuda quando a negociação de versão dá errado.
Checklist de política de segurança
Use esta lista ao escrever a política de TLS de um projeto MQTT:
- Versão mínima do protocolo: TLS 1.2 nos clientes, com TLS 1.3 permitido e preferido.
- Suites do TLS 1.2: somente troca de chaves ECDHE com AES-GCM ou ChaCha20-Poly1305.
- Suites do TLS 1.3: padrões da biblioteca, a menos que uma política exija AES-256.
- Verificação de certificado e checagem de hostname: sempre ativadas.
- Estratégia de reconexão: conexões de longa duração com backoff, não uma conexão por mensagem.
- Retomada: ativada onde a pilha suportar, para dispositivos que dormem.
- Revisão: repita os testes de versão com OpenSSL após cada atualização de broker, biblioteca ou firmware.
Manter a política curta e documentada facilita auditorias e impede que exceções "temporárias" pontuais, como uma verificação de certificado desativada, cheguem à produção.
Deixe o ajuste do servidor para um broker MQTT gerenciado
Em um broker auto-hospedado, você precisa acompanhar por conta própria descontinuações de protocolo, recomendações de cifras e renovações de certificado. Um broker MQTT gerenciado cuida do lado do servidor por você. O ConnectMQ oferece TLS na porta 8883 e WebSocket seguro na 8084 com certificado Let's Encrypt, então você só precisa da política de cliente acima: verificar o certificado, permitir TLS 1.2 ou superior e deixar a negociação escolher a melhor opção. Use o servidor MQTT público (gratuito), pensado para testes, para fazer as medições deste artigo a partir da sua própria rede.
Comece a usar o ConnectMQ
Cadastre-se no ConnectMQ em app.connectmq.com, escolha o plano Serverless gratuito, sem cartão de crédito, e crie um broker. Aponte os exemplos em Python ou Node.js acima para ele com as credenciais geradas e confira o que a sua conexão negocia.
Perguntas frequentes
O TLS 1.2 ainda é seguro para MQTT?
Sim, desde que restrito à troca de chaves ECDHE com cifras AEAD, como AES-GCM ou ChaCha20-Poly1305. Permita o TLS 1.3 junto com ele para que os clientes o usem quando estiver disponível.
O TLS 1.3 reduz o overhead das mensagens MQTT?
Não de forma significativa. O overhead por registro é parecido; os principais ganhos são um handshake mais curto, padrões mais fortes e certificados criptografados.
Qual cipher suite do TLS 1.3 os dispositivos IoT devem usar?
TLS_AES_128_GCM_SHA256 em hardware com aceleração AES e TLS_CHACHA20_POLY1305_SHA256 em CPUs sem ela. Ambas são fortes.
Devo usar early data 0-RTT com MQTT?
Em geral, não. Early data pode ser reenviado por um atacante, e os clientes MQTT ganham pouco com isso, porque as conexões são de longa duração.



