Segurança MQTT

MQTT com TLS 1.3: Handshakes, Cipher Suites e Desempenho

Como o TLS 1.3 muda as conexões MQTT: menos round trips, um conjunto menor de cipher suites fortes e outros trade-offs para microcontroladores. Inclui exemplos em Python, Node.js, Mosquitto e OpenSSL.

Ilustração de MQTT com TLS 1.3 comparando um handshake TLS 1.2 de dois round trips com um handshake TLS 1.3 de um único round trip entre dispositivo e broker
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 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:

  1. Troca de chaves: um cálculo ECDHE, normalmente na curva X25519 ou P-256. Isso acontece nas duas versões do TLS.
  2. 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.
  3. 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

  1. Mantenha as conexões abertas. Um handshake por dia sai mais barato que qualquer otimização de um handshake por minuto.
  2. Use retomada de sessão onde a sua pilha suportar, para dispositivos que precisam dormir.
  3. Ative a criptografia por hardware na configuração do seu SDK.
  4. 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.
  5. Não fixe uma única versão no firmware. Permita a negociação para que os dispositivos continuem funcionando quando os servidores mudarem.
  6. 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:

  1. Versão mínima do protocolo: TLS 1.2 nos clientes, com TLS 1.3 permitido e preferido.
  2. Suites do TLS 1.2: somente troca de chaves ECDHE com AES-GCM ou ChaCha20-Poly1305.
  3. Suites do TLS 1.3: padrões da biblioteca, a menos que uma política exija AES-256.
  4. Verificação de certificado e checagem de hostname: sempre ativadas.
  5. Estratégia de reconexão: conexões de longa duração com backoff, não uma conexão por mensagem.
  6. Retomada: ativada onde a pilha suportar, para dispositivos que dormem.
  7. 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.

Artigos relacionados

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.