Networking & Connectivity

IoT Network Security

Learn how to secure IoT devices with TLS encryption, certificate-based authentication, secure boot, and network segmentation — before your devices become attack vectors.

IoT Network Security

IoT security is not optional. A compromised IoT device can be weaponized in DDoS attacks, expose your private network, leak sensitive data, or even cause physical damage in industrial settings. This topic covers the practical security measures every IoT developer must implement.


Why IoT Devices Are Vulnerable

Common attack vectors:

1. Default credentials
   ├── Admin/admin, root/root, or no password at all
   └── Millions of devices use identical factory defaults

2. Unencrypted communication
   ├── MQTT on port 1883 (plaintext)
   └── Sniffable on any shared network

3. No update mechanism
   ├── Vulnerabilities discovered after deployment
   └── No way to patch without physical access

4. Weak authentication
   ├── Pre-shared keys hardcoded in firmware
   └── No certificate rotation

5. Exposed management interfaces
   └── Telnet, FTP, HTTP admin pages accessible from the internet

The Mirai botnet (2016) compromised 600,000 IoT devices by scanning for devices with default credentials — cameras and DVRs running Telnet with "admin/admin". It generated 1.2 Tbps of DDoS traffic, taking down GitHub, Twitter, and Netflix.


TLS/SSL for Encrypted Communication

Always use TLS for any communication leaving the local device — MQTT on port 8883, HTTPS instead of HTTP.

python
# Python — verify TLS is working correctly
import ssl
import socket

def check_tls(host, port=443):
    """Verify TLS certificate of a remote server."""
    context = ssl.create_default_context()
    # context.check_hostname = False  # NEVER do this in production!
    # context.verify_mode = ssl.CERT_NONE  # NEVER do this!

    with socket.create_connection((host, port)) as sock:
        with context.wrap_socket(sock, server_hostname=host) as ssock:
            cert = ssock.getpeercert()
            print(f"Subject:  {dict(x[0] for x in cert['subject'])}")
            print(f"Issuer:   {dict(x[0] for x in cert['issuer'])}")
            print(f"Expires:  {cert['notAfter']}")
            print(f"Protocol: {ssock.version()}")
            return True

check_tls("broker.hivemq.com", 8883)
python
# MQTT over TLS on Raspberry Pi / Python
import paho.mqtt.client as mqtt
import ssl

client = mqtt.Client()

# Option A: Server certificate verification only (simpler)
client.tls_set(
    ca_certs   = "ca.crt",          # Download from your broker
    tls_version = ssl.PROTOCOL_TLS_CLIENT
)

# Option B: Mutual TLS (mTLS) — device has its own certificate
# The broker verifies the device is who it claims to be
client.tls_set(
    ca_certs   = "ca.crt",
    certfile   = "device-cert.crt",  # Unique per device
    keyfile    = "device-key.pem",   # Private key, NEVER share
    tls_version = ssl.PROTOCOL_TLS_CLIENT
)

client.connect("your-broker.com", 8883)

Secure Credential Storage

Never hardcode passwords or API keys in firmware.

python
# BAD — credentials visible in source code and git history
WIFI_PASSWORD = "MyPassword123"
MQTT_API_KEY  = "abc123secretkey"

# GOOD — load from environment variables or a config file excluded from git
import os

WIFI_PASSWORD = os.environ.get("WIFI_PASSWORD")
MQTT_API_KEY  = os.environ.get("MQTT_API_KEY")

if not WIFI_PASSWORD:
    raise EnvironmentError("WIFI_PASSWORD not set")
python
# MicroPython ESP32 — credentials in a config.py (excluded from git)
# config.py (add to .gitignore)
WIFI_SSID     = "MyNetwork"
WIFI_PASSWORD = "MyPassword"
MQTT_BROKER   = "mybroker.example.com"
MQTT_USER     = "device-01"
MQTT_PASSWORD = "unique-per-device-token"
DEVICE_ID     = "esp32-a4f3b2"

# main.py — import safely
try:
    from config import WIFI_SSID, WIFI_PASSWORD, MQTT_BROKER
except ImportError:
    print("ERROR: config.py missing. Copy config.example.py and fill in values.")
    raise

# .gitignore
# config.py      ← never commit credentials
# *.key          ← never commit private keys
# *.pem          ← never commit certificates

Device Authentication

Username/Password (Basic)

python
# paho-mqtt client with authentication
client.username_pw_set(
    username="device-sensor-01",
    password="Str0ng!UniquePerDevice#Token"
)
# ✓ Each device should have a unique password
# ✓ Rotate passwords periodically
# ✗ Password transmitted over the wire (mitigated by TLS)

X.509 Client Certificates (Recommended)

Each device gets a unique certificate signed by your Certificate Authority (CA).

bash
# Generate CA key and certificate (run once)
openssl genrsa -out ca.key 4096
openssl req -new -x509 -key ca.key -out ca.crt -days 3650 \
    -subj "/CN=IoT-CA/O=BigXStar/C=IN"

# Generate device certificate (run for each device)
DEVICE_ID="esp32-001"
openssl genrsa -out ${DEVICE_ID}.key 2048
openssl req -new -key ${DEVICE_ID}.key -out ${DEVICE_ID}.csr \
    -subj "/CN=${DEVICE_ID}/O=BigXStar/C=IN"
openssl x509 -req -in ${DEVICE_ID}.csr \
    -CA ca.crt -CAkey ca.key -CAcreateserial \
    -out ${DEVICE_ID}.crt -days 365

# Deploy to device: ca.crt, esp32-001.crt, esp32-001.key
# Never deploy ca.key to any device!

Input Validation and Safe Parsing

python
# MQTT subscriber — always validate incoming data
import json

def parse_sensor_payload(payload_bytes):
    """Safely parse and validate incoming sensor data."""
    try:
        data = json.loads(payload_bytes.decode('utf-8'))
    except (json.JSONDecodeError, UnicodeDecodeError):
        print("Invalid JSON payload — discarded")
        return None

    # Validate required fields
    required = ['device_id', 'temperature', 'humidity']
    if not all(k in data for k in required):
        print(f"Missing fields: {required}")
        return None

    # Validate types and ranges
    try:
        device_id   = str(data['device_id'])[:64]  # Max 64 chars
        temperature = float(data['temperature'])
        humidity    = float(data['humidity'])
    except (ValueError, TypeError):
        print("Invalid field types")
        return None

    # Sanity bounds — reject physically impossible values
    if not (-40 <= temperature <= 125):
        print(f"Implausible temperature: {temperature}")
        return None

    if not (0 <= humidity <= 100):
        print(f"Implausible humidity: {humidity}")
        return None

    # Sanitize device_id (alphanumeric + dashes only)
    import re
    if not re.match(r'^[a-zA-Z0-9\-_]{1,64}$', device_id):
        print(f"Invalid device_id: {device_id}")
        return None

    return {
        'device_id':   device_id,
        'temperature': round(temperature, 2),
        'humidity':    round(humidity, 2)
    }

Network Segmentation

IoT devices should be on a separate VLAN, isolated from your main network and from the internet.

Recommended network architecture:

  Internet
     │
   Router/Firewall
     │
  ┌──┼──────────────┐
  │  │              │
Main VLAN      IoT VLAN
(192.168.1.x)  (10.0.2.x)
  │              │
  PCs, phones  Sensors, cameras,
  NAS, etc.    smart devices

Firewall rules:
  IoT VLAN → Internet:     ALLOW (port 8883 MQTT, 443 HTTPS only)
  IoT VLAN → Main VLAN:    DENY ALL
  Main VLAN → IoT VLAN:    DENY ALL (or limited management access)
  Internet → IoT VLAN:     DENY ALL (no inbound)

Firmware Security Checklist

python
# Security audit checklist as code
SECURITY_CHECKLIST = {
    "no_default_credentials":    True,   # Changed from factory defaults
    "tls_enabled":               True,   # All communication encrypted
    "certificate_auth":          True,   # mTLS where possible
    "credentials_not_hardcoded": True,   # Using env vars or secure storage
    "input_validation":          True,   # All incoming data validated
    "ota_updates_signed":        True,   # Firmware updates verified by signature
    "secure_boot":               True,   # Device only runs signed firmware
    "network_segmented":         True,   # IoT VLAN separate from main network
    "minimal_services":          True,   # No Telnet, FTP, unnecessary ports
    "logging_enabled":           True,   # Security events logged
    "physical_access_controlled": True,  # JTAG/UART debug disabled in production
}

passed = sum(SECURITY_CHECKLIST.values())
total  = len(SECURITY_CHECKLIST)
print(f"Security score: {passed}/{total} ({passed/total*100:.0f}%)")

failed = [k for k, v in SECURITY_CHECKLIST.items() if not v]
if failed:
    print(f"Action required: {', '.join(failed)}")

Common IoT Security Mistakes

MistakeRiskFix
MQTT on port 1883 (no TLS)Traffic sniffed in transitUse TLS on port 8883
Same password for all devicesOne breach = all breachedUnique credentials per device
Hardcoded credentials in firmwareExposed in reverse engineeringUse secure storage, HSM
No input validationInjection attacks, crashesValidate all external input
Debug ports enabled in productionPhysical access = full controlDisable JTAG/UART in release builds
No OTA update mechanismCan't patch vulnerabilitiesPlan OTA from day 1
IoT devices on main networkLateral movement attacksSeparate VLAN with firewall
Self-signed certs, verify disabledMITM attacksUse proper CA, never skip verification

Summary

  • IoT devices are frequent attack targets — treat security as a first-class requirement
  • Always use TLS for any communication over a network (MQTT port 8883, HTTPS)
  • Use mutual TLS (mTLS) for the strongest device authentication
  • Never hardcode credentials — use environment variables, config files in .gitignore, or secure key storage
  • Validate all incoming data — assume all external input is untrusted and potentially malicious
  • Segment your network — put IoT devices on their own VLAN with firewall rules
  • Plan for OTA (over-the-air) firmware updates from the very beginning of your project

Next, we will start programming IoT devices with MicroPython on ESP32.