Unsecured IoT devices are the easiest entry point into networks. Today you'll learn to secure devices properly — from TLS through firmware updates.
The most common IoT vulnerabilities: 1. Weak passwords (hardcoded or default). 2. Insecure network services (unnecessary open ports). 3. Insecure ecosystem interfaces (API with no auth). 4. Lack of secure update mechanism. 5. Use of insecure or outdated components. 6. Insufficient privacy protection. 7. Insecure data transfer/storage (no TLS). 8. Lack of device management. 9. Insecure default settings. 10. Lack of physical hardening.
Enable TLS on Mosquitto: generate a CA and server certificate with openssl, configure listener 8883 with certfile/keyfile/cafile in mosquitto.conf. Client connects to port 8883 with CA certificate. For HTTPS on a web API, use Let's Encrypt (Certbot) for free certificates. On ESP32: use WiFiClientSecure and provide the server certificate fingerprint or CA cert.
IoT devices must be updatable — security patches are ongoing. ESP32: Arduino OTA (ArduinoOTA.begin()) or ESP-IDF's OTA partition scheme (A/B partitions — write to B while running A, verify, then switch). Always verify firmware integrity with SHA-256 hash before flashing. Sign firmware with a private key; device verifies with embedded public key. Rollback if the new firmware fails to connect within 30 seconds.
# IoT security: TLS MQTT client + signed payload verification
# pip install paho-mqtt cryptography
import json, time, hmac, hashlib
import paho.mqtt.client as mqtt
# HMAC-based message authentication
# Device and server share a secret key
SECRET_KEY = b"your-32-byte-secret-key-here!!!!"
def sign_payload(payload_dict):
# Add HMAC-SHA256 signature to payload
body = json.dumps(payload_dict, sort_keys=True).encode()
sig = hmac.new(SECRET_KEY, body, hashlib.sha256).hexdigest()
payload_dict['sig'] = sig
return json.dumps(payload_dict)
def verify_payload(payload_str):
# Verify HMAC signature, return payload or None if invalid
try:
d = json.loads(payload_str)
received_sig = d.pop('sig', None)
if not received_sig:
return None
body = json.dumps(d, sort_keys=True).encode()
expected = hmac.new(SECRET_KEY, body, hashlib.sha256).hexdigest()
if hmac.compare_digest(received_sig, expected):
return d
return None # signature mismatch
except:
return None
# TLS MQTT client setup
def create_tls_client(client_id):
client = mqtt.Client(client_id=client_id)
# For local Mosquitto with self-signed cert:
# client.tls_set(ca_certs="/etc/mosquitto/ca.crt",
# certfile="/etc/mosquitto/client.crt",
# keyfile="/etc/mosquitto/client.key")
# client.tls_insecure_set(False)
return client
# Test HMAC signing
payload = {"temp": 23.5, "hum": 55.2, "device": "esp32-01"}
signed = sign_payload(payload.copy())
print(f"Signed: {signed}")
verified = verify_payload(signed)
print(f"Verified: {verified}")
# Tampered payload should fail
tampered = json.loads(signed)
tampered['temp'] = 99.9 # attacker changes temperature
tampered = json.dumps(tampered)
result = verify_payload(tampered)
print(f"Tampered: {result}") # None = rejected
nmap -sV localhost. Close any ports that don't need to be open.Implement certificate pinning for ESP32: instead of trusting any certificate signed by your CA, hardcode the exact server certificate fingerprint in the firmware. If the server cert changes (even from the same CA), the device rejects the connection. Research how to get the fingerprint: openssl x509 -fingerprint -sha256 -in server.crt. What are the trade-offs of pinning vs CA-trust?