Building a Secure MQTT Broker: TLS, Auth & Access Control in Production

Most IoT teams skip security at the broker layer until it's too late. Here's how to configure MQTT securely for a production-grade deployment.

By

Software Development Experts

UpNext Software is a full-cycle software development company specialising in embedded systems, IoT, mobile, and web development.

Building a Secure MQTT Broker: TLS, Auth & Access Control in Production
Article Contents

We have seen the same MQTT deployment more than once. A Mosquitto instance on port 1883, `allow_anonymous true` left in the config from the prototype phase, a public IP because the field devices needed to reach it, and a single shared username baked into every firmware image. It works. It ships. Then someone runs a Shodan search, subscribes to `#`, and reads every temperature reading, device ID and firmware update URL in the fleet. In the worse cases they publish to a command topic and something physical moves.

MQTT itself is not insecure. It is deliberately minimal — the spec assumes you will layer transport security and authorisation around it. The problem is that the defaults in most brokers are tuned for a developer on a laptop, not for a fleet in the field. This article walks through what a production-grade broker configuration actually looks like: TLS, identity, authorisation, and the operational parts nobody writes tutorials about.

Start with the threat model, not the config file

Before touching a config file, write down three things: who can reach the broker, what each device is allowed to say, and what happens if one device's credentials leak. Most MQTT security failures are failures of one of those three, not of cryptography.

A practical way to frame it for an IoT product:

  • Devices are the least trusted party. Firmware can be dumped, flash can be read, and a device in a customer's hands is a device in an attacker's hands.
  • Backend services are more trusted but should still be scoped. Your analytics consumer does not need publish rights on command topics.
  • Human operators and dashboards need the broadest access, which is exactly why they should authenticate differently from devices — and never share device credentials.
  • Compromise of one device must not equal compromise of the fleet. This is the single most useful design constraint you can impose on yourself.

That last point rules out the shared-credential shortcut immediately. If every device presents the same username and password, you have no revocation story and no audit trail. When one unit is compromised you either rotate credentials across the entire fleet — over the air, hoping nothing bricks — or you accept the breach.

TLS: what to enable and what to actually verify

MQTT over TLS is conventionally port 8883. Turning it on is easy. Turning it on correctly is where teams slip.

On the broker side, the essentials:

  • Use TLS 1.2 as your floor and prefer TLS 1.3 where your device TLS stack supports it. Disable SSLv3 and TLS 1.0/1.1 explicitly rather than trusting library defaults.
  • Restrict cipher suites to modern AEAD suites with forward secrecy (ECDHE key exchange, AES-GCM or ChaCha20-Poly1305). ChaCha20 is often faster on microcontrollers without AES hardware acceleration.
  • Get certificates from a CA your devices already trust, or run your own CA — but pick one deliberately. Mixing a public cert on the broker with a private CA bundle on devices is a classic cause of mysterious handshake failures after renewal.
  • Plan certificate rotation before launch. A broker cert that expires with no renewal path is an outage across your whole fleet on a single day.

On the device side, the part that gets skipped: verify the server certificate. Every embedded TLS library has a flag that disables verification, and it appears in an enormous amount of shipped firmware because it makes the prototype connect on the first try. Without verification, TLS gives you encryption against passive eavesdroppers and nothing at all against an active attacker who can redirect DNS or sit on the network path. Verify the chain, verify the hostname, and pin to your own CA if you control both ends.

Two practical constraints worth designing around. First, TLS needs a roughly correct clock for certificate validity checks — a device with no RTC and no NTP before its first connection will fail validation in confusing ways. Second, TLS handshakes cost RAM and airtime. On constrained cellular devices, keeping a session alive with sensible keepalives beats reconnecting constantly, and session resumption is worth enabling if your stack supports it.

Authentication: per-device identity, not a shared secret

You have three realistic options for device identity, and the right choice depends more on your provisioning process than on your broker.

Username and password per device

Simple, well supported everywhere, and fine for many products. The rules: credentials generated per device (never derived from a serial number by a formula an attacker can reverse), stored hashed on the broker side with a modern KDF, and long enough to be uninteresting to brute force. Combine with TLS so the credentials are not sent in the clear — MQTT passwords are plaintext on the wire otherwise.

Mutual TLS with client certificates

The strongest option and our default recommendation for fleets that will live for years. Each device holds its own key and certificate; the broker authenticates against your CA and derives the client identity from the certificate subject or CN. Revocation becomes a CRL or OCSP question rather than a firmware update. The cost is real: you need a provisioning step that gets a unique key onto each unit, ideally generated on-device so the private key never leaves it, and you need somewhere sensible to store it. A secure element or a microcontroller with key storage makes this genuinely robust; plain flash makes it merely better than a shared password.

Short-lived tokens

Signed tokens — JWT-style — presented in the MQTT password field, validated by the broker or an auth plugin. Nice properties: expiry is built in, and you can encode scope into claims. The catch is renewal. A device that cannot reach the token service cannot reconnect, so you need a fallback and generous expiry windows for devices on flaky links. This model suits deployments where devices are already talking to an application API for other reasons.

Whichever you choose, enforce one more rule: bind the client ID to the authenticated identity. Otherwise one device can connect with another's client ID and, because MQTT client IDs are unique per broker, forcibly disconnect it. That is a trivial denial-of-service against your own fleet.

Authorisation: topic ACLs are where security actually happens

Authentication answers "who are you." Access control answers "what may you touch," and it is the layer that contains a breach. Design your topic hierarchy so authorisation is expressible as a pattern, not a list of exceptions.

A structure that works well in practice:

  • `tenant/{tenant}/device/{deviceId}/telemetry` — device publishes, backend subscribes.
  • `tenant/{tenant}/device/{deviceId}/cmd` — backend publishes, device subscribes. Devices never publish here.
  • `tenant/{tenant}/device/{deviceId}/status` — retained last-will topic for presence.
  • Broadcast topics kept separate and read-only for devices, so a compromised unit cannot address the fleet.

Then write ACLs using the broker's identity substitution — Mosquitto's `%c` and `%u` patterns, or the equivalent in EMQX, HiveMQ or VerneMQ — so a device is confined to topics containing its own identifier. Deny by default. Never grant a device subscribe access to a multi-level wildcard, and be careful with `$SYS` topics, which leak broker internals and connected client lists.

Two extra guards worth adding. Cap the number of subscriptions and the maximum message size per client so a misbehaving device cannot exhaust broker memory. And decide explicitly whether devices may set retained messages — a compromised device that publishes a huge retained payload leaves a mess that outlives its connection.

Choosing and running the broker

Mosquitto is excellent, lightweight and easy to reason about; it fits single-node deployments and edge gateways well, though clustering means fronting it with something else. EMQX and VerneMQ cluster natively and expose richer auth hooks. HiveMQ is the common choice where commercial support and enterprise integration matter. Managed options — AWS IoT Core, Azure IoT Hub — hand you mutual TLS, per-device identity and policy-based authorisation out of the box, and if your team is small that trade is often the honest recommendation. You give up some protocol flexibility and pay per message; you get a security posture you would otherwise spend weeks building.

If you self-host, the operational baseline:

  • Terminate TLS at the broker rather than a proxy where you can, so client certificates reach the authorisation layer intact. If you must proxy, ensure identity is forwarded rather than lost.
  • Bind the plaintext 1883 listener to localhost only, or remove it. Do not leave it open "for testing."
  • Put WebSocket listeners for browser dashboards behind their own authentication path — they are a different trust class from devices.
  • Run the broker as an unprivileged user, in a container or with systemd hardening, with persistence on a volume you actually back up.
  • Log authentication failures, ACL denials and connection churn, and alert on spikes. A device retrying a denied publish thousands of times a minute is telling you something.
  • Rate-limit connections per source. Reconnect storms after a network blip look a lot like an attack and cause the same damage.

Provisioning is the hard part, and it is not a broker problem

Everything above assumes each device arrives in the field with a unique credential. Getting there is a manufacturing and backend problem, and it is where most projects stall.

The patterns that hold up: generate the key pair on the device during factory test and have it sign a certificate request against your CA; or write a one-time provisioning token at flash time that the device exchanges for permanent credentials on first boot, then discards. Both avoid a database of private keys sitting in your build system. Both require the factory line and your backend to talk to each other, which is a conversation worth having early rather than two weeks before production.

Also decide now what revocation looks like. Who can disable a device, how quickly it takes effect, and whether an already-connected session is terminated or allowed to persist. If the answer is "we'd have to redeploy the broker config," you do not yet have a revocation process.

A short pre-launch checklist

  1. Anonymous access disabled and confirmed by attempting an anonymous connect from outside your network.
  2. Plaintext listener closed or bound to localhost.
  3. TLS 1.2+ only, modern cipher suites, certificate expiry monitored with an alert weeks ahead.
  4. Devices verify the server certificate and hostname — checked in firmware, not assumed.
  5. Per-device credentials, client ID bound to identity, no shared secrets in the image.
  6. ACLs deny by default and confine each device to its own topic subtree; wildcard subscribe denied.
  7. Message size, subscription count and connection rate limits set.
  8. Auth failures and ACL denials logged, shipped somewhere, and alerted on.
  9. A written, tested revocation procedure.
  10. A load test that includes a full-fleet reconnect, because that is the scenario that breaks brokers.

None of this is exotic. It is a couple of days of careful configuration and a provisioning design decision made early rather than late. The reason it gets skipped is that MQTT works beautifully without any of it, right up until it doesn't.

If you are designing an IoT backend and want a second pair of eyes on the broker, topic design or provisioning flow — or you need engineers who have shipped this kind of thing to join an existing team — get in touch. You can also see the kind of systems we build in our work, or read about how we structure an embedded team around a hardware roadmap.

Continue Reading
Related Articles