Why IoT devices are attacked

An IoT device is a small computer that is always on, always connected, rarely looked at and often years behind on updates. It sits in a meter cupboard or on a shelf, with nobody watching its logs. It is cheap, so its maker saved on everything that does not show on the box, and it lives for five to fifteen years, far longer than the attention of the team that wrote its firmware. For an attacker that is an attractive combination: millions of identical targets, each one weak in the same way.

What attackers want from such a device:

  • Its bandwidth. Thousands of hijacked cameras and routers together form a botnet that can flood a website or a network with a distributed denial of service attack.
  • A way in. A device on the home or office network can reach the laptops, file shares and other devices next to it.
  • Its data. A camera's images, a door lock's log, or a smart meter's readings: power use per second shows when people are home, asleep or cooking (see privacy and the P1 port).
  • Its effect on the physical world. A device that switches a heat pump, a battery or a lock can do harm that a hacked laptop cannot. That is where IoT meets OT security.

Mirai: the botnet made of defaults

Mirai is the botnet that made IoT security front-page news. On 20 September 2016 it hit the website of security journalist Brian Krebs with about 620 Gbit/s of traffic, nearly twice the largest attack Akamai had seen until then. On 21 October 2016 it attacked Dyn, a DNS provider, and made many well-known websites unreachable for parts of a day. At its peak it controlled about 600,000 devices: mostly IP cameras, video recorders and home routers.

The lesson is in how it spread. Mirai did not use any unknown vulnerability. It scanned the internet for devices with an open Telnet port and tried a list of just 62 username and password combinations, the factory defaults of a handful of device makers. Whatever accepted one of them became a bot. Its source code was published on 30 September 2016, and many variants followed; some added exploits for known router vulnerabilities, but the default passwords stayed at the core.

The descendants are still growing. As of October 2026 the largest publicly reported DDoS attack is 31.4 Tbit/s, reported by Cloudflare for the last quarter of 2025 and attributed to a botnet made up mainly of hijacked Android TV devices: fifty times the Krebs attack, built from the same kind of neglected device.

Five layers of IoT security, stacked from the device up: the device itself (secure boot, signed firmware, keys in a secure element), the home network (own SSID or VLAN, no inbound ports), the connection (TLS on port 8883 with a certificate per device), the broker and platform (no anonymous access, a per-device access control list, logging), and the lifecycle around all of them (updates, vulnerability reports, a support period and revocation). Lifecycle updates, vulnerability reports, support period Broker, platform no anonymous access, per-device ACL, logging Connection TLS on 8883, a certificate per device Home network own SSID or VLAN, no inbound ports Device secure boot, signed firmware, keys in a secure element
Defense in depth for a connected device. No single layer is enough: a signed firmware does not help if the broker accepts anyone, and a strict broker does not help if the password is "admin".

This page follows the Kestrel Home pilot, which started in September 2026: about 250 households with a dynamic contract received a P1 dongle, built around an ESP32-C3 by an outside manufacturer and sold under Kestrel's name. With the customer's consent, the dongle sends live power from the smart meter's P1 port to Kestrel's MQTT broker. Lotte Kortenhoeve gave her consent on 14 September 2026; her dongle, serial KHP1-000231, is paired with home h0017. The design, MQTT over TLS with a certificate per dongle, is in ADR-0011, which is still proposed.

Default passwords and unique credentials

A universal default password is a password that is the same on every unit of a product when it leaves the factory, such as admin or 12345. Users do not change it, because they do not know it exists or because the device works fine without changing it. Once the password is in one manual or one forum post, every unit is open.

The rule that came out of Mirai is simple: a device either has a password that is unique per unit, or it forces the user to set one before it can be used. "Unique" has a catch. Passwords that are made from the serial number, the MAC address or a counter (kestrel000302) are unique, but anyone who sees the label or one example can work out all the others. ETSI EN 303 645 (below) calls those out explicitly. The UK has made the same rule law since 29 April 2024 (the Product Security and Telecommunications Infrastructure regime), and in the United States a California law in effect since 1 January 2020 requires connected devices to have reasonable security and treats a unique or user-set password as meeting that requirement.

A Kestrel dongle has no login, but it does have a password: the one for its setup hotspot, printed on the label, which a customer uses once to give the dongle the home Wi-Fi details. For a planned second batch, Kestrel checks the manufacturer's production file before the labels are printed. A check like this one catches the usual problems:

from collections import Counter

# Passwords that ship on many devices of many brands.
UNIVERSAL = {"admin", "password", "default", "root", "1234", "12345", "123456"}

# Setup-hotspot passwords in a manufacturer's production file (invented test data).
batch = [
    ("KHP1-000301", "a0:76:4e:1c:03:2d", "admin"),
    ("KHP1-000302", "a0:76:4e:1c:03:2e", "kestrel000302"),
    ("KHP1-000303", "a0:76:4e:1c:03:2f", "setup-1c032f"),
    ("KHP1-000304", "a0:76:4e:1c:03:30", "tq8V-mz3K-wr9L"),
    ("KHP1-000305", "a0:76:4e:1c:03:31", "Lp4x-7HcN-eB2s"),
    ("KHP1-000306", "a0:76:4e:1c:03:32", "Lp4x-7HcN-eB2s"),
]


def problems(serial, mac, password, seen):
    found = []
    pw = password.lower()
    if pw in UNIVERSAL:
        found.append("universal default")
    if serial.split("-")[1] in pw:
        found.append("derived from the serial number")
    if mac.replace(":", "")[-6:] in pw:
        found.append("derived from the MAC address")
    if seen[password] > 1:
        found.append(f"shared by {seen[password]} devices")
    if len(password) < 12:
        found.append("shorter than 12 characters")
    return found


seen = Counter(password for _, _, password in batch)
rejected = 0
for serial, mac, password in batch:
    found = problems(serial, mac, password, seen)
    if found:
        rejected += 1
        print(f"{serial}  REJECT  {'; '.join(found)}")
    else:
        print(f"{serial}  ok")
print(f"{rejected} of {len(batch)} rejected")
KHP1-000301  REJECT  universal default; shorter than 12 characters
KHP1-000302  REJECT  derived from the serial number
KHP1-000303  REJECT  derived from the MAC address
KHP1-000304  ok
KHP1-000305  REJECT  shared by 2 devices
KHP1-000306  REJECT  shared by 2 devices
5 of 6 rejected

The 12-character minimum is Kestrel's own rule, not one from a standard. The check can only find patterns it knows; it cannot prove that a password is random. The real protection is in how the manufacturer generates them: from a cryptographically secure random source on the production line, one per unit, never reused.

Secure boot and signed firmware

A device's firmware is everything it is. Whoever can replace it controls the device, with all its keys and all its access. Two mechanisms stop that: the device only installs firmware with a valid signature, and it only starts firmware with a valid signature. The PC version of the second idea is Secure Boot; microcontrollers have their own.

Firmware signing

Firmware signing means the manufacturer's build system adds a digital signature to every firmware image, made with a private key that never leaves the build system. The device holds only the matching public key. It computes the hash of a downloaded image and checks the signature against it. A damaged image, an image changed by an attacker or an image built by someone else fails the check and is not installed. Because the device holds only the public key, reading a dongle's flash gives an attacker nothing to sign with.

A second check protects against an old, genuine, but vulnerable version: the device keeps a minimum version that only ever goes up, and refuses anything older. Without it, an attacker could "downgrade" a patched device to a signed release with a known hole. (This is different from the automatic rollback on Provisioning and Over-the-Air Updates: that one returns to the previous slot when a new image fails its self-test.)

The example below shows the decisions a device makes. Python's standard library has no public-key signatures, so it uses HMAC, a keyed hash (see request signing), as a stand-in. That is fine for showing the logic and wrong for real firmware: with HMAC the device needs the same secret key that creates signatures, so one opened device would let an attacker sign firmware for all of them.

import hashlib
import hmac

# STAND-IN for real firmware signing. HMAC uses ONE secret key for signing
# and checking, so every device would hold the key that can sign. Real
# firmware signing uses a key pair: the private key stays in the build
# system, the device only holds the public key (see the text).
BUILD_KEY = b"invented-demo-key-not-a-real-one"


def sign(image, version):
    digest = hashlib.sha256(image).hexdigest()
    tag = hmac.new(BUILD_KEY, f"{version}:{digest}".encode(), "sha256")
    return {"version": version, "sha256": digest, "signature": tag.hexdigest()}


def as_tuple(version):
    return tuple(int(part) for part in version.split("."))


def check(image, manifest, min_version):
    digest = hashlib.sha256(image).hexdigest()
    if digest != manifest["sha256"]:
        return "reject: hash does not match the manifest"
    expected = hmac.new(BUILD_KEY, f"{manifest['version']}:{digest}".encode(), "sha256")
    if not hmac.compare_digest(expected.hexdigest(), manifest["signature"]):
        return "reject: signature does not match"
    if as_tuple(manifest["version"]) < as_tuple(min_version):
        return f"reject: {manifest['version']} is older than {min_version}"
    return f"accept {manifest['version']}"


image = b"KHP1 firmware 1.4.0 " * 4096          # stands in for an 80 KB image
manifest = sign(image, "1.4.0")
print("sha256", manifest["sha256"][:16], "...")

# 1. The genuine release.
print("genuine:      ", check(image, manifest, "1.3.2"))

# 2. One byte changed in transit or in storage.
damaged = bytearray(image)
damaged[1000] ^= 0x01
print("damaged:      ", check(bytes(damaged), manifest, "1.3.2"))

# 3. Someone patches the image AND puts the new hash in the manifest.
patched = image.replace(b"1.4.0", b"6.6.6", 1)
forged = dict(manifest, sha256=hashlib.sha256(patched).hexdigest())
print("forged:       ", check(patched, forged, "1.3.2"))

# 4. A genuine but old release with a known vulnerability.
old = b"KHP1 firmware 1.2.0 " * 4096
print("old, genuine: ", check(old, sign(old, "1.2.0"), "1.3.2"))
sha256 5eaf8a65b4156c06 ...
genuine:       accept 1.4.0
damaged:       reject: hash does not match the manifest
forged:        reject: signature does not match
old, genuine:  reject: 1.2.0 is older than 1.3.2

Case 3 is why a hash alone is not enough. A hash proves that the image matches the manifest, but whoever changes the image can also change the manifest. Only the signature ties both to a key the attacker does not have. The math page makes the same point about checksums: they catch accidents, not attacks.

Secure boot on microcontrollers

Signature checks during an update protect the update path. Secure boot protects the device even against someone who writes to its flash directly, with a cable on the serial port. The chain works like this:

  1. Code in the chip's read-only memory (ROM), which nobody can change, checks the signature of the bootloader against a public key, or a hash of it, that was burned into one-time programmable memory (eFuses or OTP) in the factory.
  2. The bootloader checks the signature of the application.
  3. Anything that fails the check does not run. A device with an unsigned image on it simply does not start that image.

Espressif's chips call this Secure Boot v2 (with RSA-PSS signatures; the original ESP32 supports it from chip revision 3.0). The Raspberry Pi RP2350 checks a signed boot image against a key fingerprint stored in its OTP memory. Most chips pair secure boot with flash encryption, so that reading the flash chip does not reveal the firmware or the secrets stored next to it. Espressif's flash encryption has a development mode, in which the device can still be reflashed over the serial port, and a release mode, which closes that door for good.

Secure elements

A device's private key, the one behind its certificate, must stay secret for the whole life of the device. Encrypted flash helps, but the main processor can still read the key, and so can any bug in its firmware. A secure element is a separate, tamper-resistant chip (or an isolated part of a chip) that generates and stores keys and uses them on request: the firmware asks it to sign, and gets back a signature, never the key. It is the IoT relative of the TPM in a PC and of the chip in a bank card.

Not every product needs one. A secure element costs money, board space and engineering time; for a cheap sensor, a key in encrypted flash with secure boot may be a reasonable trade-off. For a device whose key opens something valuable, such as a door lock, a payment terminal, or a gateway with access to many other devices, it is the expected choice. Programs such as PSA Certified (levels 1 to 4, where Level 4 iSE/SE is aimed at secure elements) test and rate how well a chip and its firmware protect such secrets.

Communicating securely

Plain MQTT on port 1883 sends everything in clear text: readings, topics, and the username and password in the CONNECT packet. Anyone on the same Wi-Fi or anywhere along the path can read it. Production traffic uses TLS, by convention on port 8883. In the Kestrel Home pilot each dongle also proves its identity with its own certificate (mutual TLS), issued by "Kestrel Home Device CA 1" and valid from 18 August 2026 to 15 August 2036. How the dongles receive those certificates is on Provisioning and Over-the-Air Updates; how MQTT itself works is on MQTT and IoT Messaging.

Three things on the device side are easy to get wrong:

  • The device must check the broker's certificate. TLS without that check encrypts the traffic to whoever answers, including an attacker in the middle. Disabling verification "to get it working" during development and forgetting to turn it back on is a classic.
  • The device needs the right time. A certificate is valid between two dates, and a microcontroller without a battery-backed clock starts in 1970 after every power cut. It must get the time, usually over NTP, before it can check a certificate.
  • Certificates expire. A fleet whose certificates all expire on the same day stops on that day. Plan renewal long before (see certificate lifetimes).

Authentication at the broker

The broker is the front door of the whole fleet. Before the pilot started, its authorization rules were tried on a test broker. The output below was recorded with Mosquitto 2.0.11 (the Debian 12 package) on 7 October 2026, on the loopback address and without TLS, so that every packet is easy to read; production uses port 8883 with TLS. For the same reason the test uses usernames and passwords; in the pilot the identity comes from the dongle's certificate, but the rules about who may do what are the same. The configuration:

listener 1883 127.0.0.1
allow_anonymous false
password_file /etc/mosquitto/passwd
acl_file /etc/mosquitto/acl
persistence false
sys_interval 10
log_dest file /var/log/mosquitto/mosquitto.log
log_type all

allow_anonymous false refuses every client that does not log in, and password_file names the file with the users and their password hashes. log_type all is there only to make the test log show every packet; leave it out in production. Users are added with mosquitto_passwd, which asks for the password instead of taking it on the command line, where it would end up in the shell history (add -c the first time, to create the file):

mosquitto_passwd /etc/mosquitto/passwd p1-h0017

A client that does not log in is refused:

mosquitto_sub -h 127.0.0.1 -p 1883 -t '#'
Connection error: Connection Refused: not authorised.

A wrong password gives exactly the same message, and the broker log shows the same line for both: Client <unknown> disconnected, not authorised. That is deliberate. If the answers differed, an attacker could find out which usernames exist. (With MQTT 5, the client says Not authorized instead, for both cases.)

Per-device permissions

Logging in is not enough. If every dongle may publish anywhere, one stolen dongle can send fake readings for all 250 homes. The broker's access control list (ACL) gives each identity only what it needs:

# Dashboard: read-only on all homes, plus broker statistics
user dashboard
topic read kestrel/home/#
topic read $SYS/#

# P1 dongle of home h0017: may only write its own topics
user p1-h0017
topic write kestrel/home/h0017/#

# P1 dongle of home h0018
user p1-h0018
topic write kestrel/home/h0018/#

The dongle of home h0017 may publish under kestrel/home/h0017/ and nowhere else; it may not read anything. The topic contains the pilot home number, never the EAN code or the customer number, so a topic name in a log is not personal data by itself. In the pilot such a file would not be written by hand but generated from the device registry, which knows which dongle belongs to which home.

Here the h0017 dongle tries to publish a reading for home h0018, with MQTT 3.1.1 (the default) and -d to show the packets:

mosquitto_pub -d -h 127.0.0.1 -p 1883 -u p1-h0017 -P "$MQTT_PASSWORD" -i p1-h0017 -q 1 -t kestrel/home/h0018/p1/power -m '{"time":"2026-10-07T19:32:00+02:00","watts":99999}'
Client p1-h0017 sending CONNECT
Client p1-h0017 received CONNACK (0)
Client p1-h0017 sending PUBLISH (d0, q1, r0, m1, 'kestrel/home/h0018/p1/power', ... (50 bytes))
Client p1-h0017 received PUBACK (Mid: 1, RC:0)
Client p1-h0017 sending DISCONNECT

The broker dropped the message: a dashboard subscribed to kestrel/home/+/p1/power received nothing. But the publisher got a normal acknowledgment and exited with status 0. With MQTT 3.1.1 an acknowledgment has no field to say "refused", so the broker acknowledges and drops the message, and the publisher cannot tell. MQTT 5 can say it:

mosquitto_pub -V mqttv5 -d -h 127.0.0.1 -p 1883 -u p1-h0017 -P "$MQTT_PASSWORD" -i p1-h0017 -q 1 -t kestrel/home/h0018/p1/power -m '{"time":"2026-10-07T19:32:00+02:00","watts":99999}'
Client p1-h0017 sending CONNECT
Client p1-h0017 received CONNACK (0)
Client p1-h0017 sending PUBLISH (d0, q1, r0, m1, 'kestrel/home/h0018/p1/power', ... (50 bytes))
Client p1-h0017 received PUBACK (Mid: 1, RC:135)
Warning: Publish 1 failed: Not authorized.
Client p1-h0017 sending DISCONNECT

Reason code 135 (hexadecimal 0x87) is MQTT 5's "Not authorized". Even so, the exit status was 0, so a script must not rely on it. And with QoS 0 there is no acknowledgment at all, so a refused QoS 0 publish is silent in both versions.

Seeing denials in the broker log

A denied publish is a security event: a device trying to write where it should not is either broken or no longer under its owner's control. With log_type all, the broker log shows it (the first block is the 3.1.1 attempt, the second the MQTT 5 one; the numbers at the start are Unix time, here a little after 19:32 on 7 October 2026):

1791394347: New connection from 127.0.0.1:36052 on port 1883.
1791394347: New client connected from 127.0.0.1:36052 as p1-h0017 (p2, c1, k60, u'p1-h0017').
1791394347: No will message specified.
1791394347: Sending CONNACK to p1-h0017 (0, 0)
1791394347: Denied PUBLISH from p1-h0017 (d0, q1, r0, m1, 'kestrel/home/h0018/p1/power', ... (50 bytes))
1791394347: Sending PUBACK to p1-h0017 (m1, rc135)
1791394347: Received DISCONNECT from p1-h0017
1791394347: Client p1-h0017 disconnected.
1791394347: New connection from 127.0.0.1:36062 on port 1883.
1791394347: New client connected from 127.0.0.1:36062 as p1-h0017 (p5, c1, k60, u'p1-h0017').
1791394347: No will message specified.
1791394347: Sending CONNACK to p1-h0017 (0, 0)
1791394347: Denied PUBLISH from p1-h0017 (d0, q1, r0, m1, 'kestrel/home/h0018/p1/power', ... (50 bytes))
1791394347: Sending PUBACK to p1-h0017 (m1, rc135)
1791394347: Received DISCONNECT from p1-h0017
1791394347: Client p1-h0017 disconnected.

p2 and p5 are Mosquitto's notation for MQTT 3.1.1 and MQTT 5. The broker writes reason code 135 in both cases, but only the MQTT 5 client can receive it. The catch is the Denied PUBLISH line: it is a debug-level message. Here is a separate short run with the same configuration without the log_type line, which is how most brokers run: a refused anonymous client, an allowed publish, a denied MQTT 5 publish (the third connection), and two more clients that disconnect:

1791394429: mosquitto version 2.0.11 starting
1791394429: Config loaded from /etc/mosquitto/mosquitto.conf.
1791394429: Opening ipv4 listen socket on port 1883.
1791394429: mosquitto version 2.0.11 running
1791394430: New connection from 127.0.0.1:42250 on port 1883.
1791394430: Client <unknown> disconnected, not authorised.
1791394430: New connection from 127.0.0.1:42260 on port 1883.
1791394430: New client connected from 127.0.0.1:42260 as p1-h0017 (p2, c1, k60, u'p1-h0017').
1791394430: Client p1-h0017 disconnected.
1791394430: New connection from 127.0.0.1:42264 on port 1883.
1791394430: New client connected from 127.0.0.1:42264 as p1-h0017 (p5, c1, k60, u'p1-h0017').
1791394430: Client p1-h0017 disconnected.
1791394430: New connection from 127.0.0.1:42280 on port 1883.
1791394430: New client connected from 127.0.0.1:42280 as p1-h0017 (p2, c1, k60, u'p1-h0017').
1791394431: Client p1-h0017 closed its connection.
1791394432: New connection from 127.0.0.1:42286 on port 1883.
1791394432: New client connected from 127.0.0.1:42286 as p1-h0017 (p2, c1, k60, u'p1-h0017').
1791394433: Client p1-h0017 disconnected.
1791394434: mosquitto version 2.0.11 terminating

The refused login is there, but the denied publish looks exactly like the allowed one: connect, disconnect. In Mosquitto 2.0.11 at the default log level, an ACL violation leaves no trace.

Reducing the attack surface

Every open port, service and debug interface is a way in. A device should offer as little as possible:

  • No listening services it does not need. The Kestrel dongle only makes outgoing connections: to the broker on port 8883, to the update server, and to DNS and NTP servers for names and the time. It opens no port of its own once set up; its setup hotspot switches off after pairing. No Telnet, no web interface that stays on "for support".
  • No debug access in production. The serial console and the JTAG debug interface are invaluable on the bench and a gift to anyone who opens a production device. Disable or lock them in the release configuration.
  • No port forwarding from the internet. A device that needs to be reachable from outside should connect out to a broker or platform instead (see cloud-to-device messages). Automatic port opening with UPnP on the home router is best switched off.
  • Validate all input. A device parses data it did not create: telegrams, MQTT messages, settings from an app. A parser that trusts its input can be crashed or worse (see fuzzing).

Some interfaces are safe by design. The smart meter's P1 port only sends: nothing a dongle does on the P1 port can change the meter. A hacked dongle can leak a household's power use, but it cannot touch the meter or the grid.

Segmenting IoT networks

Assume that one device will be compromised one day, and decide what it can reach then. That is network segmentation. At home it means putting IoT devices on their own Wi-Fi network, such as a guest network or a separate SSID, so that a cheap plug with old firmware cannot reach the laptop with the tax return. Lotte has done this: her ESP32 P1 reader, the Raspberry Pi 5 with Home Assistant and the Kestrel dongle are on an IoT network with WPA2/WPA3; her laptop and phone are not. Because her Home Assistant has to talk to the devices, they share that segment; one firewall rule on her router lets her phone reach the Home Assistant dashboard, and nothing else on that network.

In a building or a company, the same idea uses VLANs and firewall rules: cameras, access control, the building management system and meeting-room screens each in their own segment, allowed to reach only the servers they need. Where the devices control physical processes, the OT approach of zones and conduits applies, and microsegmentation takes it down to single devices.

ETSI EN 303 645: the baseline

ETSI EN 303 645 is the European standard for the cyber security of consumer IoT devices, from the European Telecommunications Standards Institute (ETSI). It started as a technical specification in February 2019, became a European standard in June 2020, and the current version is V3.1.3 of September 2024. It is voluntary: it is not a law by itself, but laws and certification schemes build on it, and buyers ask for it. A companion document, ETSI TS 103 701, describes how to assess whether a product conforms.

Its 13 provisions read like a summary of this page. What they would mean for the Kestrel Home dongle:

Provision For the pilot dongle
5.1 No universal default passwords A random setup password per unit, checked on delivery
5.2 Implement a means to manage reports of vulnerabilities A published contact and a process to handle reports (see below)
5.3 Keep software updated Signed OTA updates, staged as in CHG-5012
5.4 Securely store sensitive security parameters Private key generated on the device, never exported
5.5 Communicate securely MQTT over TLS on 8883, certificate checked both ways
5.6 Minimize exposed attack surfaces No listening ports, debug interfaces locked
5.7 Ensure software integrity Secure boot and firmware signing
5.8 Ensure that personal data is secure Readings encrypted in transit; topics without EAN
5.9 Make systems resilient to outages Keeps reading the meter when the broker is down; reconnects with jitter
5.10 Examine system telemetry data Watch for denied publishes and odd reconnect patterns
5.11 Make it easy for users to delete user data Leaving the pilot deletes the data (REQ-3120)
5.12 Make installation and maintenance of devices easy Plug into P1, pair in the app (KB-0610)
5.13 Validate input data The telegram parser rejects malformed input

Clause 6 adds data protection provisions, such as telling users what personal data the device processes and why. Provision 5.11 is already being tested: on 7 October 2026 Lotte asked Kestrel to stop her participation in the pilot at the end of October and delete her data then (REQ-3120). The steps, from revoking the dongle's certificate to deleting her readings, are on Provisioning and Over-the-Air Updates.

Outside the EU, the same ideas show up in other forms: the UK's product security regime (unique passwords, a way to report vulnerabilities, a published update period) and, in the United States, NIST IR 8259 Rev. 1 (April 2026) for manufacturers and the voluntary Cyber Trust Mark label, whose lead administrator withdrew in December 2025 and was replaced in April 2026; check its current status before relying on it.

Handling vulnerability reports

Provision 5.2 is the one companies most often lack. Someone who finds a hole in the dongle needs to know where to report it, and needs to trust that the report will be handled instead of ignored or answered by a lawyer. That means a published policy, a contact such as a security.txt file, an internal owner for each report, and fixed response times; see responsible disclosure. Fixed vulnerabilities get a CVE identifier and a public advisory. To know whether a new vulnerability in a library affects the dongle at all, Kestrel needs a software bill of materials from the manufacturer for every firmware version.

The law

Whoever puts a connected product on the market has legal duties, and they differ per region, as of October 2026:

In the EU, two laws turn much of the baseline into obligations for whoever puts a connected product on the market: the radio equipment rules now, the Cyber Resilience Act (CRA) from 11 December 2027. They apply one after the other, as the sections below explain.

The United States has no general federal law that sets security requirements for consumer IoT products. Narrower federal laws cover IoT devices bought by federal agencies (the IoT Cybersecurity Improvement Act of 2020) and medical devices (FDA rules since 2023). For other products, what exists:

  • NIST IR 8259 Rev. 1, "Foundational Cybersecurity Activities for IoT Product Manufacturers" (final, 20 April 2026): guidance, not law.
  • The U.S. Cyber Trust Mark: a voluntary label of the Federal Communications Commission (FCC) for consumer wireless IoT products. Its first lead administrator, UL Solutions, withdrew effective 19 December 2025; the FCC selected the ioXt Alliance on 13 April 2026. Check whether labeled products are available before relying on it.
  • State laws: California's connected-devices law (in effect since 1 January 2020) requires a reasonable security feature and counts a password that is unique per device, or a new one the user must set before first access, as one.

A US manufacturer that sells in the EU or the UK must meet the rules there.

In the United Kingdom, the product security regime under the Product Security and Telecommunications Infrastructure (PSTI) Act 2022 and its 2023 regulations has applied since 29 April 2024 to consumer connectable products. It sets three security requirements:

  • No universal default passwords: passwords are unique per product or set by the user.
  • A published way to report security issues, with information on when the reporter will get an acknowledgment and status updates.
  • The minimum period of security updates, published clearly.

The Office for Product Safety and Standards (OPSS) enforces it; penalties can reportedly reach £10 million or 4% of worldwide revenue. A UK manufacturer that sells in the EU must also meet the EU rules there.

Radio equipment rules

The Radio Equipment Directive (RED, Directive 2014/53/EU) sets the rules every product with a radio transmitter must meet before it can be sold in the EU, from a garage door opener to a phone. It was originally about electrical safety, electromagnetic compatibility and efficient use of the radio spectrum. A delegated regulation, (EU) 2022/30, added cyber security requirements for wireless devices that connect to the internet, process personal data, or handle money; it has applied since 1 August 2025.

The practical way to meet them is a harmonized standard in three parts: EN 18031-1:2024 for internet-connected radio equipment, -2 for equipment that processes personal data, and -3 for equipment that handles virtual money. A product that follows the applicable parts is presumed to comply, with restrictions set when the EU cited the standards in January 2025: for example, a product that lets the user choose not to set any password gets no presumption of conformity from the standard.

For Kestrel this is not a future problem. A Wi-Fi dongle sold today is radio equipment, so these rules apply to it now. The RED cyber security rules are temporary, though: they are repealed with effect from 11 December 2027, when the Cyber Resilience Act takes over.

These EU rules apply to every wireless product sold in the EU today that connects to the internet, processes personal data or handles money, wherever it was made.

In the United States, FCC equipment authorization, which a radio device needs before it is sold, is about radio emissions and interference, not cybersecurity. The FCC's cybersecurity program for devices is the voluntary Cyber Trust Mark. A US manufacturer selling a wireless device in the EU must meet the RED rules there.

EN 18031 belongs to the EU rules. In the United Kingdom, the security requirements for a consumer connected device come from the product security regime described above, which applies with or without a radio. A UK manufacturer selling a wireless device in the EU must meet the RED rules there.

Vulnerability reporting and lifecycle duties

The Cyber Resilience Act (CRA, Regulation (EU) 2024/2847) covers almost every product with digital elements sold in the EU, with or without a radio. Its general shape is on the Security and OT pages. For IoT devices, the dates and duties that matter are:

Date What applies
10 December 2024 CRA in force (nothing applies yet)
1 August 2025 RED cyber security rules for wireless products (EN 18031)
11 June 2026 Rules for the bodies that assess products (notified bodies)
11 September 2026 Manufacturers must report actively exploited vulnerabilities and severe incidents, also for products already on the market
11 December 2027 All other obligations, for products placed on the market from then on; the RED cyber rules end

The reporting duty has tight deadlines. When a manufacturer learns that a vulnerability in its product is being actively exploited, it sends an early warning within 24 hours, a fuller notification within 72 hours, and a final report no later than 14 days after a fix or workaround is available, through the single reporting platform run by ENISA, the EU cyber security agency. That only works if the vulnerability process above already exists.

From December 2027 the main requirements include: no known exploitable vulnerabilities at release, secure default settings, security updates (automatic by default, with a way to opt out), a software bill of materials covering at least the top-level dependencies, and a declared support period. Most products assess themselves; some categories need more. Smart home products with a security function, such as door locks, security cameras, baby monitors and alarm systems, are "important" products (Class I), and smart meter gateways are among the "critical" ones.

Outside medical devices, no US law requires IoT manufacturers to report vulnerabilities in their products to an authority or to meet lifecycle requirements. Guidance such as NIST IR 8259 Rev. 1 describes the activities a manufacturer should plan for, such as handling vulnerability reports and deciding how long to support a product, and buyers increasingly ask for them in contracts. The vulnerability process above is still worth building: it is what customers, and the EU and UK rules for anything sold there, expect.

The UK product security regime has no duty to report vulnerabilities to an authority. It does require a published way for anyone to report security issues, with information on when the reporter will get an acknowledgment and status updates, which is the vulnerability process described above in legal form. Other lifecycle duties, such as a software bill of materials or secure defaults, are not in the regime; ETSI EN 303 645 covers them as a voluntary baseline.

The support period

A support period is the time during which a manufacturer promises to fix vulnerabilities in a product and deliver security updates. What the law says about it differs per region:

Under the CRA the support period has to be stated when the product is sold, and it is at least five years, unless the product is expected to be in use for less time.

No US law requires a support period or a minimum length. A stated support period is still a fair question for any supplier, and it is required for products sold in the EU or the UK.

The UK product security regime requires the minimum period of security updates to be published clearly, but sets no minimum length.

For a device that people keep in a meter cupboard for ten years, the question is not only the legal minimum but also what happens to the device afterward (compare end-of-life systems). A planning check against the CRA minimum, with invented scenarios:

from datetime import date

CRA_MINIMUM_YEARS = 5
CERT_VALID_UNTIL = date(2036, 8, 15)   # device certificates of the pilot CA


def add_years(day, years):
    try:
        return day.replace(year=day.year + years)
    except ValueError:                 # 29 February, target year has none
        return day.replace(year=day.year + years, day=28)


def review(placed, expected_use, planned):
    minimum = min(CRA_MINIMUM_YEARS, expected_use)
    end = add_years(placed, planned)
    if planned < minimum:
        return end, f"TOO SHORT: minimum is {minimum} years"
    if end > CERT_VALID_UNTIL:
        return end, f"ok, but certificates expire {CERT_VALID_UNTIL} first"
    return end, "ok"


# Planning scenarios (invented): placed on the market, expected years of
# use, planned years of security updates.
scenarios = [
    ("dongle, 8-year plan", date(2026, 10, 7), 8, 8),
    ("dongle, 3-year plan", date(2026, 10, 7), 8, 3),
    ("dongle, sold 29 Feb 2028", date(2028, 2, 29), 8, 5),
    ("dongle, 10-year plan", date(2027, 1, 15), 10, 10),
    ("party gadget", date(2026, 10, 7), 2, 2),
]

for name, placed, expected_use, planned in scenarios:
    end, verdict = review(placed, expected_use, planned)
    print(f"{name:25} updates until {end}  {verdict}")
dongle, 8-year plan       updates until 2034-10-07  ok
dongle, 3-year plan       updates until 2029-10-07  TOO SHORT: minimum is 5 years
dongle, sold 29 Feb 2028  updates until 2033-02-28  ok
dongle, 10-year plan      updates until 2037-01-15  ok, but certificates expire 2036-08-15 first
party gadget              updates until 2028-10-07  ok

The fourth line is the kind of problem that is cheap to find on paper and expensive in the field: the dongles would still be supported, but their certificates, valid until 15 August 2036, would stop working first. Either the certificates are renewed over the air before then, or the support promise is shorter than it looks. The support period also counts from when each unit is sold, so a product sold for years has a support end that moves with it.

A security checklist for a device fleet

Before a connected product goes to customers, and again before a pilot grows into a product, go through these questions:

  • Credentials: is every password unique per unit or set by the user, and is no password derived from the serial or MAC address?
  • Firmware: is secure boot on in release mode, is every update signed, and is the signing key stored and backed up safely?
  • Keys: is each device's private key generated on the device and kept in a secure element or encrypted flash?
  • Connections: is all traffic encrypted, does the device check the server's certificate, and does it have the right time?
  • Broker: is anonymous access off, and may each device publish only to its own topics? Can you see denied attempts?
  • Network: does the device open no listening ports, and is it on its own segment?
  • Lifecycle: is there a published way to report vulnerabilities, an SBOM per firmware version, a declared support period, and a decommissioning process that revokes the certificate?
  • Law: who is the manufacturer, and which product rules apply where you sell: in the EU the RED rules today and the CRA's reporting duty since 11 September 2026, in the UK the product security regime, in the US state laws such as California's?