An MQTT broker that accepts anonymous connections on port 1883 lets anyone on the network read every sensor reading and publish commands to every device subscribed to a topic. MQTT is the default messaging protocol for IoT and increasingly for industrial telemetry, from building sensors to Sparkplug-based SCADA integration. The protocol itself leaves security to the implementation: the specification says brokers should offer authentication, authorisation and secure communication, and strongly advises using them for critical infrastructure and personal data, but does not force any of it. This guide covers broker authentication, topic-level authorisation, TLS, client credential management, detection and the failure patterns seen in real deployments. It is for engineers who run brokers (Mosquitto, EMQX, HiveMQ and similar) or use cloud IoT services, and for security teams assessing them.
Key takeaways
- Anonymous access is the first thing to check. Mosquitto 2.0 changed the default so anonymous clients are refused unless explicitly allowed; brokers configured on 1.6.x or earlier, or copied from old tutorials, often still allow them.
- Authentication without authorisation is not enough. A broker that checks passwords but grants every client read and write on
#lets one stolen device credential reach the whole fleet. Scope every client to its own topics. - TLS on 8883 is the baseline, client certificates are the target. Passwords over plaintext 1883 can be sniffed; per-device X.509 certificates remove shared secrets and bind identity to the client ID.
- Every device, gateway and backend service is a non-human identity. Shared fleet passwords and backend "superuser" accounts are the MQTT equivalent of a shared admin password.
- Check what "deny" actually does. Under MQTT 3.1.1 a broker may silently drop an unauthorised publish, and some brokers ignore denials by default, so failures do not show up unless you log and monitor them.
How MQTT works and where the risks sit
MQTT is a publish and subscribe protocol: clients connect to a broker, publish messages to topics (for example site1/line3/press/temperature) and subscribe to topic filters that may use wildcards (+ for one level, # for all remaining levels). The current versions are MQTT 3.1.1 (OASIS, October 2014, also published as ISO/IEC 20922:2016) and MQTT 5.0 (OASIS Standard, 7 March 2019). The IANA ports are 1883 for plain TCP and 8883 for MQTT over TLS.
| MQTT feature | What it does | Security consequence |
|---|---|---|
Wildcard subscriptions (#, +) | Subscribe to many topics at once | A client allowed to subscribe to # receives every message on the broker, including commands and credentials sent in payloads |
| Retained messages | Broker keeps the last message per topic for new subscribers | Stale or malicious retained commands are delivered to every device that reconnects |
| Will messages | Broker publishes on the client's behalf if it disconnects uncleanly | A client that may set any will topic can publish to topics it is not otherwise allowed to |
| Client ID | Identifies the session | A second connection with the same client ID disconnects the first, so a guessed ID can knock a device offline (session takeover) |
$SYS topics | Broker statistics and version | Reveals broker software, version and client counts to anyone allowed to subscribe |
| Bridges | Broker to broker forwarding | Bridge credentials often have wide topic rights and sit in configuration files |
| MQTT over WebSockets | Browser and firewall-friendly transport | A second listener with its own authentication settings, often forgotten in reviews |
The threats map to a small set of ATT&CK for ICS techniques: Internet Accessible Device (T0883) for brokers reachable from the internet, Default Credentials (T1694.001) for factory or tutorial passwords, Network Sniffing (T0842) and Adversary-in-the-Middle (T0830) on plaintext links, and Unauthorized Message: Command Message (T1692.001) when an attacker publishes to a command topic.
Broker authentication options
The MQTT CONNECT packet carries a client ID and optional username and password. MQTT 5.0 adds enhanced authentication: an Authentication Method property (0x15), Authentication Data (0x16) and an AUTH packet, so a broker can run challenge and response schemes and ask a client to re-authenticate (reason code 0x19) during a long-lived session.
| Method | How it works | Strengths | Weaknesses | Use when |
|---|---|---|---|---|
| Anonymous | No credentials | None | Anyone who can reach the port is a client | Never on shared networks; only local loopback test brokers |
| Username and password | Checked against a password file, database or plugin | Universally supported | Shared secrets, often reused across a fleet; exposed without TLS | Constrained devices without certificate support, always over TLS, one credential per device |
| TLS client certificate | Broker validates the certificate against a CA; CN or subject becomes the username | No password to steal or reuse; revocable per device | Needs enrolment, renewal and revocation processes | Production fleets and all backend services |
| TLS-PSK | Pre-shared key per identity | Lighter than certificates | Keys are shared secrets held on both sides | Very constrained devices that cannot do certificates |
| Token (JWT, SAS) | Signed token in the password field or via enhanced auth | Short lived, can carry claims | Signing key becomes a high-value secret; expiry must be enforced | Cloud IoT services and brokers integrated with an identity provider |
| External plugin or HTTP | Broker calls an auth service | Central policy, integrates with existing identity stores | Auth service availability becomes broker availability | Large deployments with an existing IAM or device registry |
Product specifics worth knowing:
- Mosquitto:
allow_anonymousdefaults to false in 2.x unless no listener is defined, in which case only local connections are accepted. Versions 1.6.x and earlier defaulted to true. Withper_listener_settings false(the default) authentication settings are global, so a WebSockets listener added later inherits them; with it set to true each listener needs its own settings.require_certificate trueplususe_identity_as_username truemakes the certificate CN the username. - AWS IoT Core: the device certificate identifies the thing, and thing policy variables such as
iot:Connection.Thing.ThingNameandiot:Connection.Thing.IsAttachedlet one policy restrict each device to its own client ID and topics, provided the client ID equals the thing name. - Azure IoT Hub: MQTT 3.1.1 on 8883 or over WebSockets on 443, TLS 1.2 required and plain 1883 not supported. The client ID is the device ID and the password is a SAS token; Microsoft's documentation recommends X.509 certificates for production. IoT Hub is not a general-purpose broker: topics are fixed per device.
MQTT 5.0 also gives the broker defensive options: close connections that do not send CONNECT promptly, and on public networks close a failed connection without sending a CONNACK so an attacker cannot confirm a broker is there or iterate passwords quickly.
Topic authorisation and ACL design
Authorisation decides what an authenticated client may publish, subscribe to and receive. Design topics so that ACLs can be written with substitution variables rather than one rule per device.
- Put identity in the topic: a structure like
tenant/site/<clientid>/telemetryandtenant/site/<clientid>/cmdlets one rule scope every device. Mosquitto ACL files supportpatternlines with%c(client ID) and%u(username); EMQX uses${clientid}and${username}. - Separate publish and subscribe: devices publish telemetry and subscribe only to their own command topic; backend services subscribe to telemetry and publish commands. No device needs
#. - Deny by default: check what happens when no rule matches. EMQX's
no_matchdefault changed to deny in EMQX 6.0, and its sample ACL file ends with an allow-all rule that must be removed. The Mosquitto Dynamic Security plugin defaults to deny for publish and subscribe but allow for receiving messages on topics already subscribed. - Restrict
$SYSand will topics: limit$SYSto monitoring accounts and make sure will messages are subject to the same publish ACL as normal publishes. - Revoke cleanly: in EMQX, changing a rule does not revoke existing subscriptions; delete the client's session as well. In the Mosquitto Dynamic Security plugin, disabling a client disconnects its sessions.
- Industrial namespaces: the Eclipse Sparkplug 3.0.0 specification (November 2022) uses topics of the form
spBv1.0/group_id/message_type/edge_node_id/device_idand suggests ACLs so each edge node can publish only on topics for its own group and edge node ID, which makes spoofing harder. Only the Primary Host Application should be allowed to publish command (NCMD, DCMD) messages.
| Client type | Publish | Subscribe | Notes |
|---|---|---|---|
| Field device or sensor | …/%c/telemetry, …/%c/status | …/%c/cmd | One credential per device; no wildcards |
| Edge gateway | Topics for devices behind it | Commands for those devices | Scope to the gateway's site or group |
| Ingestion service | None or acknowledgements | …/+/telemetry | Read-only on telemetry; separate from command service |
| Command service | …/+/cmd | Status topics | Highest-risk credential: protect like a privileged account |
| Monitoring | None | $SYS/# | No access to business topics |
| Bridge | Only forwarded topics | Only forwarded topics | Explicit topic list, never # in both directions |
TLS configuration
- Listeners: bind 1883 to loopback only (or remove it) and expose 8883. Audit every listener, including WebSockets and any management or HTTP API listener.
- Versions: set a minimum of TLS 1.2 (Mosquitto
tls_version tlsv1.2) and prefer 1.3 where clients support it. Bridges need TLS too: in Mosquitto at least one ofbridge_cafile,bridge_capathorbridge_tls_use_os_certsmust be set. - Server authentication on the client: devices must validate the broker certificate and hostname. Disabling verification to "make it work" (for example an insecure flag in the client library) turns TLS into encryption to an unknown party.
- Client certificates: issue from a dedicated device CA, one certificate per device, with the client ID in the CN or SAN, and a defined renewal path. See the IoT and Device Certificate Provisioning Guide and the mTLS Guide.
- Revocation: confirm the broker checks a CRL or equivalent; otherwise removing a stolen device means rotating the CA.
Where identity fits: client credentials as NHIs
Every MQTT client is a non-human identity, and fleets create them by the thousand. The common anti-pattern is one username and password baked into firmware for every device of a model, plus a backend service account with read and write on # stored in a configuration file or container image.
- Inventory: list every broker identity with owner, type (device, gateway, service, bridge), authentication method, topic rights and last connection time. Remove identities that have not connected in 90 days.
- Unique per device: provision credentials or certificates individually at manufacture or first boot so one extracted credential compromises one device. See the Identity for IoT Fleets Guide.
- Backend secrets in a vault: service and bridge credentials belong in a secrets manager with rotation, not in
mosquitto.conf, environment files or source control. See the Secrets Management Guide. - Bootstrap files: Mosquitto's
mosquitto_ctrl dynsec initwrites generated initial passwords in plain text to a file that should be deleted once used. - Management APIs: admin consoles, the Dynamic Security control topic
$CONTROL/dynamic-security/v1and broker REST APIs can create clients and change ACLs. Restrict them to named administrators.
Rolling out MQTT security on an existing broker
- Discover: scan your own address space for 1883 and 8883 and list every broker, listener and version. Treat any internet-reachable broker as an incident until proven otherwise. See the Attack Surface Management Guide.
- Log before you enforce: enable connection and subscribe logging (Mosquitto
log_typeincludessubscribe) and collect a week of client IDs, usernames and topic use. - Write ACLs from observed behaviour: draft per-role ACLs from the logs, deploy them on a staging broker and replay traffic.
- Turn on TLS alongside plaintext: add an 8883 listener, migrate clients in rings (lab devices, one site, then the fleet), then close 1883.
- Disable anonymous access and enforce ACLs: in a maintenance window, with the previous configuration file kept so the broker can be reverted in minutes.
- Move to per-device certificates: as devices are replaced or reflashed, switching from shared passwords to certificates.
- Set limits: connection limits, maximum packet size (Mosquitto
max_packet_sizedefaults to 2,000,000 bytes from 2.1) and connection rate limits to reduce denial-of-service risk.
Self-hosted vs cloud IoT service: a cloud service such as AWS IoT Core or Azure IoT Hub enforces TLS and per-device identity by design but restricts topic structures; a self-hosted broker gives full MQTT 5 features and on-premises operation (common in OT) but makes every control your responsibility. Small deployments with tens of devices can manage a password file and static ACL; fleets in the thousands need a device registry, certificate automation and an authorisation plugin or database.
Detection and measurement
- Authentication failures: CONNACK reason codes 0x86 (bad user name or password), 0x87 (not authorized) and 0x8C (bad authentication method) in broker logs; alert on bursts from one source.
- Session takeovers: repeated disconnects with reason 0x8E (session taken over) for the same client ID suggest a cloned credential or a duplicate device.
- Wildcard subscriptions: any subscription to
#or$SYS/#by an identity not on the approved list. - Publish to command topics: commands from identities other than the command service.
- New identities: first-seen client IDs and usernames, especially outside provisioning windows.
Metrics: brokers reachable from the internet (target zero); listeners without TLS (target zero outside loopback); percentage of clients using per-device certificates; identities with wildcard rights; identities unused for 90 days; denied operations per day by identity, with each persistent one investigated. See the OT Network Security Monitoring Guide.
Framework mapping
| MQTT control | NIST SP 800-53 Rev. 5 | NIST CSF 2.0 | Threat (ATT&CK for ICS v20) |
|---|---|---|---|
| Per-device authentication, no anonymous access | IA-3, IA-5 | PR.AA-03 | T1694.001 Default Credentials |
| Topic ACLs, deny by default | AC-3, AC-6 | PR.AA-05 | T1692.001 Command Message |
| TLS on all listeners and bridges | SC-8 | PR.DS-02 | T0842 Network Sniffing, T0830 |
Remove unused listeners, restrict $SYS | CM-7, CM-6 | PR.PS-01 | T0883 Internet Accessible Device |
| Connection and packet limits | SC-5 | PR.PS-01 | Denial of service |
| Broker and auth logging | AU-12, SI-4 | DE.CM-01 | Detection of the above |
| Broker and identity inventory | CM-8 | ID.AM-01 | Unmanaged brokers |
Common failure patterns
- Tutorial configuration in production: a broker built from an old guide with
allow_anonymous trueon 1883, then exposed when a firewall rule is opened for a cloud dashboard. - One fleet password: the same username and password in every device's firmware; extracting it from one device unlocks the broker for all of them.
- Allow-all ACL left in place: authentication is configured, but the default authoriser or sample ACL still allows everything, so every client can subscribe to
#. - Second listener, no security: a WebSockets listener added for a web dashboard with different or no authentication settings.
- Disabled certificate verification: device code that skips broker certificate validation, accepting any server on the path.
- Silent denials: brokers configured to ignore unauthorised publishes without logging, so a misbehaving or compromised client is never noticed.
No live NHI Mgmt Group breach page currently covers an MQTT broker exposure, so none is linked here.
Practitioner checklist
- Scan for brokers on 1883 and 8883 across your ranges and close any that are reachable from the internet.
- Confirm anonymous access is disabled on every listener, including WebSockets and bridges.
- Enforce TLS 1.2 or later on every listener and bridge, and verify that clients validate the broker certificate.
- Issue one credential or certificate per device and per service; retire shared fleet passwords.
- Write deny-by-default ACLs using client ID or username substitution; remove allow-all defaults.
- Restrict command topics to the command service and
$SYSto monitoring accounts. - Move backend and bridge credentials into a secrets manager with rotation.
- Log authentication failures, session takeovers and wildcard subscriptions, and alert on them.
- Set connection limits, packet size limits and rate limits.
- Review broker identities quarterly and remove those unused for 90 days.
Standards and references
- OASIS: MQTT Version 5.0
- OASIS: MQTT Version 3.1.1
- Eclipse Mosquitto: mosquitto.conf man page
- Eclipse Mosquitto: Dynamic Security plugin
- EMQX: Authorization
- AWS: AWS IoT Core thing policy variables
- Microsoft Learn: Communicate with an IoT hub using the MQTT protocol
- Eclipse Foundation: Sparkplug 3.0.0 Specification
Related NHI Mgmt Group resources: IoT Security Guide · Industrial IoT (IIoT) Security Guide · Device and IoT Identity Guide · Industrial Protocol Security Guide · Kafka Security Guide