Segurança MQTT

Limitação de Taxa no MQTT e Proteção contra DoS Explicadas

Como os brokers MQTT se defendem de inundações de conexões, tempestades de publicação e payloads enormes, quais limites um broker MQTT gerenciado aplica e como escrever clientes que os respeitam.

Diagrama de limitação de taxa MQTT com um broker contendo uma tempestade de publicação enquanto dispositivos IoT normais continuam enviando mensagens
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

A limitação de taxa no MQTT (rate limiting) é a forma como um broker protege a si mesmo e a seus usuários contra clientes que enviam demais, rápido demais ou grande demais. Este artigo explica os principais padrões de negação de serviço no MQTT, os limites que os brokers usam para contê-los, como a ConnectMQ aplica cotas e proteção contra inundação e como escrever clientes que recuam em vez de agravar o problema. A maioria dos incidentes de limite de taxa é causada por firmware com bugs, e não por atacantes, como uma frota de medidores de uma distribuidora de energia que reconecta toda de uma vez após uma queda, por isso os dois lados importam.

Por que o MQTT precisa de limitação de taxa

Um broker MQTT é um recurso compartilhado. Cada conexão consome memória, cada mensagem consome CPU e banda, e cada assinatura acrescenta trabalho de roteamento. Sem limites, um único cliente com mau comportamento pode degradar o serviço para todos os outros clientes do mesmo broker.

A ameaça não é apenas maliciosa. Causas comuns de sobrecarga incluem:

  • Um bug de firmware que publica em um loop apertado em vez de uma vez por minuto.
  • Um loop de reconexão sem atraso após uma falha de rede, repetido em toda a frota.
  • Uma câmera ou um registrador publicando, por engano, grandes blobs binários.
  • Um dashboard assinando # em um broker movimentado e replicando todas as mensagens.
  • Tentativas de adivinhar credenciais que abrem conexão após conexão.

A limitação de taxa transforma essas falhas, que seriam indisponibilidades, em erros contidos e visíveis.

Os principais padrões de DoS no MQTT

Padrão O que acontece Defesa típica
Inundação de conexões Muitas conexões TCP/TLS abertas rapidamente, cada uma custando um handshake e memória Limites de taxa de conexão por IP, máximo de conexões por tenant, bloqueio de IP
Tempestade de reconexões Milhares de dispositivos se reconectam no mesmo instante após uma queda Backoff exponencial com jitter no lado do cliente
Tempestade de publicação Um cliente envia muito mais mensagens por segundo do que o esperado Limites de taxa de mensagens por cliente, desconexão em caso de abuso
Payloads grandes demais Mensagens enormes consomem memória e banda Tamanho máximo de pacote
Explosão de assinaturas Um cliente cria muitas assinaturas ou curingas amplos Máximo de assinaturas por cliente, limites de profundidade de topic
Inchaço de retained ou de sessões Muitos topics retidos ou sessões longas com mensagens enfileiradas Limites de retained messages, expiração de sessão
Consumidores lentos Assinantes que não acompanham o ritmo fazem as filas crescerem Limites de fila, escolhas de QoS, expiração de mensagens
Adivinhação de credenciais Logins com falha repetidos Limitação de logins com falha e bloqueio de IP

Como os brokers aplicam limites

Limites no nível de conexão

O ponto mais barato para impedir abusos é antes de o cliente ser autenticado. Os brokers limitam quantas conexões um único IP pode abrir por segundo, restringem o total de conexões por conta ou plano e bloqueiam origens que falham repetidamente na autenticação. Um handshake TLS é relativamente caro, então limitar nessa etapa protege tanto a CPU quanto a memória.

Limites no nível de mensagem

Depois de conectado, o broker pode acompanhar mensagens por segundo por cliente e bytes por segundo. Quando um cliente excede o orçamento, o broker tem algumas opções: atrasar o processamento, descartar mensagens, recusá-las com um reason code ou desconectar o cliente. O MQTT 5 torna isso muito mais transparente do que o MQTT 3.1.1.

Limites de tamanho de pacote

Todo broker tem um tamanho máximo de pacote. O MQTT 5 permite que os dois lados o anunciem: o broker envia Maximum Packet Size no CONNACK, e os clientes nunca devem enviar mais do que isso. Uma mensagem acima do limite é um erro de protocolo e normalmente resulta em desconexão.

Limites de assinaturas e de topics

Tetos para assinaturas por cliente e para a profundidade dos topics impedem que clientes criem trabalho de roteamento ilimitado.

Recursos do MQTT 5 que ajudam

O MQTT 5 adicionou vários mecanismos que tornam os limites explícitos em vez de misteriosos. A visão geral dos recursos do MQTT 5 os aborda com mais detalhes.

  • Reason codes. Um broker pode responder com 0x97 Quota exceeded em um PUBACK ou DISCONNECT, ou com 0x96 Message rate too high, em vez de descartar dados silenciosamente. Os clientes podem registrar o motivo e desacelerar.
  • Receive Maximum. Os dois lados anunciam quantas mensagens QoS 1 e QoS 2 sem confirmação aceitam em trânsito, o que oferece um controle de fluxo básico.
  • Maximum Packet Size. Anunciado no CONNECT e no CONNACK para que mensagens grandes demais sejam evitadas, e não rejeitadas.
  • Expiração de mensagens. Mensagens antigas são descartadas em vez de se acumularem para assinantes offline.
  • Expiração de sessão. Sessões abandonadas são limpas automaticamente.
  • Server Keep Alive. O broker pode sobrescrever o intervalo de keep-alive do cliente.

Com o MQTT 3.1.1, um broker que aplica limites normalmente só consegue descartar mensagens ou fechar a conexão, e o cliente vê um erro de rede genérico.

Limites na ConnectMQ

A ConnectMQ aplica limites em várias camadas para proteger os tenants em seus clusters compartilhados:

  • Cotas por plano. O plano Serverless (Free) permite até 25 conexões, QoS até 1 e mensagens de 256 KB. O plano Serverless (Paid) amplia isso para até 1.000 conexões, QoS 2 e mensagens de 1 MB. As implantações Dedicated e BYOC rodam em infraestrutura isolada.
  • Sinalização de cota. Quando uma cota é excedida, clientes MQTT 5 recebem o reason code 0x97 (quota exceeded), para que você consiga distinguir um limite de uma falha de rede.
  • Proteção contra inundação para padrões abusivos de conexão e de mensagens.
  • Bloqueio de IP após logins com falha repetidos.
  • Isolamento de tenants, para que o tráfego de um tenant não alcance os topics de outro.

O servidor MQTT público, broker.connectmq.com, tem limites por cliente mais rígidos porque está aberto a todos: 64 KB por mensagem, 10 mensagens por segundo por cliente, 10 assinaturas por cliente, 8 níveis de topic e expiração de sessão de até 30 minutos. Se um teste atingir esses limites, é um forte indício de que a carga deve ir para um broker privado. O artigo sobre o servidor MQTT público (gratuito) lista os detalhes; lembre-se de que ele serve para testes e protótipos, não para dados de produção.

Escrevendo clientes que respeitam limites

Os limites no servidor são só metade da história. Uma frota de clientes bem-comportados é a melhor proteção contra DoS que você pode ter.

  1. Faça backoff na reconexão. Use backoff exponencial com jitter aleatório para que os dispositivos não se reconectem em sincronia após uma queda.
  2. Agrupe e agregue. Envie uma mensagem com dez leituras em vez de dez mensagens, quando a latência permitir.
  3. Publique quando houver mudança, não a cada volta do loop. Adicione bandas mortas (deadbands) para que pequenas flutuações não gerem tráfego.
  4. Respeite o tamanho de pacote anunciado. Leia Maximum Packet Size das propriedades do CONNACK.
  5. Trate os reason codes. Registre 0x97 e 0x96 e reduza sua taxa de publicação em vez de tentar de novo imediatamente.
  6. Mantenha as assinaturas restritas. Assine os topics de que você precisa, não #.
  7. Use client IDs únicos. Dois dispositivos compartilhando um ID derrubam um ao outro em um loop infinito que parece uma inundação.

Aqui está um cliente Python usando paho-mqtt 2.x que aplica backoff, trata reason codes de cota e limita a própria publicação:

import json
import random
import time
import paho.mqtt.client as mqtt

HOST, PORT = "broker.connectmq.com", 8883
CLIENT_ID = f"rate-demo-{random.randint(1000, 9999)}"
TOPIC = f"connectmq/demo/{CLIENT_ID}/telemetry"
MAX_RATE = 2.0  # mensagens por segundo, bem abaixo do limite do broker público

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID,
                     protocol=mqtt.MQTTv5)
client.tls_set()
# Para um broker privado, adicione: client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.reconnect_delay_set(min_delay=1, max_delay=120)  # backoff exponencial

slow_down_until = 0.0

def on_connect(c, userdata, flags, reason_code, properties):
    print("connected:", reason_code)
    if properties and hasattr(properties, "MaximumPacketSize"):
        print("broker max packet size:", properties.MaximumPacketSize)

def on_publish(c, userdata, mid, reason_code, properties):
    global slow_down_until
    if reason_code.is_failure:
        print(f"publish {mid} refused: {reason_code}")
        if reason_code.value in (0x96, 0x97):
            slow_down_until = time.time() + 30  # pausa de 30 s

def on_disconnect(c, userdata, flags, reason_code, properties):
    print("disconnected:", reason_code)

client.on_connect = on_connect
client.on_publish = on_publish
client.on_disconnect = on_disconnect

client.connect(HOST, PORT, keepalive=60)
client.loop_start()

interval = 1.0 / MAX_RATE
try:
    while True:
        if time.time() < slow_down_until:
            time.sleep(1)
            continue
        payload = json.dumps({"t": round(20 + random.random(), 2), "ts": int(time.time())})
        client.publish(TOPIC, payload, qos=1)
        time.sleep(interval)
except KeyboardInterrupt:
    client.loop_stop()
    client.disconnect()

Os mesmos princípios valem para firmware. Em um ESP32, coloque um atraso com jitter antes de cada tentativa de reconexão e nunca chame publish() dentro de um loop sem um temporizador; o tutorial de MQTT com ESP32 mostra um padrão não bloqueante.

Monitorando problemas de limite de taxa

Fique atento a estes sinais nos logs dos clientes e nos dashboards do broker:

  • Picos de desconexões em muitos dispositivos no mesmo instante.
  • Reason codes 0x97 ou 0x96 repetidos vindos do mesmo cliente.
  • Dispositivos que conectam, publicam e desconectam em um ciclo de poucos segundos.
  • Uma taxa de mensagens que não corresponde ao intervalo de envio configurado.

Na ConnectMQ, o dashboard do portal e o explorador de topics ao vivo ajudam a identificar um cliente "tagarela", e $SYS/broker/connection/{clientId}/state permite acompanhar o estado de conexão de um cliente específico.

Quando migrar para um plano maior

Se é tráfego legítimo, e não um bug, que está atingindo os limites, a resposta é capacidade, e não clientes mais restritos. Os sinais incluem crescimento constante de dispositivos conectados, necessidade de QoS 2 ou payloads acima de 256 KB. O guia de broker MQTT gerenciado explica como as opções serverless e dedicadas diferem, e o artigo relacionado sobre brokers MQTT expostos mostra por que brokers auto-hospedados sem limites são um risco.

Comece a usar a ConnectMQ

Crie um broker MQTT gerenciado no plano Serverless (Free) em app.connectmq.com sem cartão de crédito, conecte o cliente acima com as credenciais geradas e veja na prática como cotas e reason codes se comportam. Para orientações mais amplas, leia as melhores práticas de segurança MQTT.

Perguntas frequentes

O que significa o reason code 0x97 do MQTT?

Significa "Quota exceeded" (cota excedida). O broker está informando ao cliente que ele ultrapassou um limite permitido, então o cliente deve desacelerar em vez de tentar de novo imediatamente.

O MQTT 3.1.1 oferece feedback de limite de taxa?

Não diretamente. O MQTT 3.1.1 não tem reason codes no PUBACK, então um broker só pode descartar mensagens ou desconectar. Atualize os clientes para MQTT 5 para receber feedback explícito.

Devo limitar a taxa também no dispositivo?

Sim. Limitação no lado do cliente, agrupamento de mensagens e backoff na reconexão evitam a maioria dos incidentes e mantêm você bem dentro dos limites do broker.

O que é uma tempestade de reconexões?

Acontece quando muitos dispositivos perdem a conexão ao mesmo tempo e todos tentam de novo imediatamente. Backoff exponencial com jitter aleatório distribui as reconexões ao longo do tempo.

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.