I still remember the first time I watched a colleague plug a JTAG debugger into a “secure” payment terminal and pull the entire firmware image off in under two minutes. No encryption, no read-out protection, nothing. That moment changed how I think about embedded design forever. Security isn’t a feature I bolt on at the end of a project — it’s a property that has to be designed into the silicon, the bootloader, the firmware, and the communication stack from day one. In this article I want to walk through why security matters so much in embedded systems, how it’s implemented in practice, and what a professional embedded workflow for security actually looks like.
Why Embedded Security Is Different From IT Security
When people talk about cybersecurity, they usually picture servers, laptops, and cloud infrastructure. Embedded systems are a different animal entirely. I’m talking about the microcontroller in a pacemaker, the ECU in a car, the smart meter on the side of a house, the industrial PLC running a water treatment plant. These devices:
- Run for years or decades without a reboot or OS reinstall
- Often have no user interface to show a security warning
- Are physically accessible to attackers (unlike a data center server)
- Have tight memory, power, and compute budgets that make heavyweight cryptography difficult
- Frequently can’t be patched in the field once deployed
Because of this, a vulnerability in an embedded device isn’t just a data breach risk — it can mean physical harm, infrastructure failure, or a botnet of a hundred thousand IoT cameras (which is exactly what happened with the Mirai botnet in 2016).
The Embedded Attack Surface
Before I can defend a system, I need to understand where it’s exposed. I generally break the attack surface into four layers.
graph TD
A[Physical Layer] --> B[Hardware/Silicon Layer]
B --> C[Firmware/Software Layer]
C --> D[Communication Layer]
A -->|JTAG, UART, power analysis, chip decapping| A1[Physical Attacks]
B -->|Side-channel, fault injection, glitching| B1[Hardware Attacks]
C -->|Buffer overflows, insecure bootloader, weak keys| C1[Firmware Attacks]
D -->|MITM, replay, spoofing, sniffing| D1[Network Attacks]
Physical attacks happen when an attacker has the device in their hands. Exposed debug ports (JTAG/SWD), unencrypted flash, and accessible UART headers are the classic entry points. Hardware attacks get more sophisticated — power analysis (measuring current draw to infer key bits) and voltage/clock glitching (forcing the CPU to skip an instruction, like a security check) are real techniques used against smart cards and secure elements. Firmware attacks exploit software bugs: stack overflows in a poorly written parser, or a bootloader that will happily flash any image handed to it. Communication attacks target the wire — Wi-Fi, BLE, LoRa, CAN bus — intercepting or forging messages.
Core Security Principles for Embedded Design
I try to anchor every embedded security decision in a few well-established principles:
- Root of Trust – a hardware-anchored starting point (fuses, secure boot ROM, or a secure element) that cannot be modified, from which all higher-level trust is derived.
- Defense in Depth – no single control is trusted alone; secure boot, encrypted storage, and communication security should all be present simultaneously.
- Least Privilege – firmware components run with only the memory and peripheral access they actually need (this is where MPUs/MMUs and TrustZone come in).
- Fail Secure – if a check fails (signature verification, integrity check), the device should refuse to boot or operate rather than degrade gracefully into an insecure state.
Secure Boot Chain
Secure boot is the practice of cryptographically verifying every stage of the boot process before it’s allowed to run.
sequenceDiagram
participant ROM as Boot ROM (immutable)
participant BL1 as Stage-1 Bootloader
participant BL2 as Stage-2 Bootloader
participant APP as Application Firmware
ROM->>ROM: Verify BL1 signature using burned-in public key hash
ROM->>BL1: Jump to BL1 if valid
BL1->>BL1: Verify BL2 signature
BL1->>BL2: Jump to BL2 if valid
BL2->>BL2: Verify APP signature
BL2->>APP: Jump to APP if valid
Note over ROM,APP: Chain of trust — each stage verifies the next
Here’s a simplified example of how a bootloader might verify a firmware image signature before jumping to it, using a typical embedded crypto library pattern:
#include <stdint.h>
#include <string.h>
#include "crypto_ecdsa.h" /* vendor-provided ECDSA verify routine */
#define FW_IMAGE_ADDR 0x08010000U
#define FW_SIG_ADDR 0x0801F800U
#define PUB_KEY_ADDR 0x0BF90000U /* stored in OTP / secure region */
typedef void (*app_entry_t)(void);
int verify_and_boot(void)
{
const uint8_t *fw_image = (const uint8_t *)FW_IMAGE_ADDR;
const uint8_t *signature = (const uint8_t *)FW_SIG_ADDR;
const uint8_t *pub_key = (const uint8_t *)PUB_KEY_ADDR;
uint32_t fw_len = 0x0F800; /* size of application image */
/* 1. Hash the firmware image (SHA-256) */
uint8_t digest[32];
sha256(fw_image, fw_len, digest);
/* 2. Verify ECDSA signature against the stored public key */
if (ecdsa_verify(pub_key, digest, sizeof(digest), signature) != CRYPTO_OK) {
/* Fail secure: do not boot, optionally erase RAM, halt or reset */
return -1;
}
/* 3. Signature valid — jump to application */
uint32_t app_stack = *(uint32_t *)(FW_IMAGE_ADDR);
uint32_t app_reset = *(uint32_t *)(FW_IMAGE_ADDR + 4);
__set_MSP(app_stack);
app_entry_t app_entry = (app_entry_t)app_reset;
app_entry();
return 0; /* unreachable */
}
This pattern is exactly what STM32’s TrustZone/HSE, NXP’s SE050, and ESP32-S3’s secure boot v2 implement under the hood, just with vendor-specific tooling around it.
Hardware-Level Security Features I Look For
When I’m choosing a microcontroller for a security-sensitive project, I check for:
- Secure Boot ROM – immutable first-stage bootloader burned into silicon
- Cryptographic accelerators – hardware AES, SHA, RSA/ECC engines so crypto doesn’t eat the whole CPU budget
- True Random Number Generator (TRNG) – needed for key generation and nonces; software PRNGs are not acceptable for security
- Secure Key Storage – OTP fuses, eFuse, or a dedicated secure element (e.g., ATECC608A, SE050)
- Memory Protection Unit (MPU) or TrustZone-M – hardware-enforced isolation between trusted and untrusted firmware
- Read-out protection (RDP) – prevents flash contents from being dumped via debug interfaces
- Tamper detection pins – can trigger key erasure if the enclosure is opened
STM32L5 and STM32U5 series implement Arm TrustZone-M, splitting the MCU into a Secure World and a Non-Secure World at the hardware level — a non-secure application literally cannot read secure-world memory, even with a bug.
Firmware-Level Security Practices
Hardware gives me the foundation, but most real-world embedded vulnerabilities are firmware bugs. Practices I follow:
- Static analysis and MISRA-C compliance to catch buffer overflows and undefined behavior before they ship
- Stack canaries and MPU-guarded stack regions to detect and stop overflow-based exploits
- Encrypted and authenticated OTA updates — every firmware update should be signed and encrypted, and the bootloader should reject anything that doesn’t verify
- Anti-rollback counters stored in one-time-programmable memory, so an attacker can’t downgrade firmware to a version with a known vulnerability
- Watchdog timers as a last line of defense against firmware getting stuck (whether from a bug or a fault-injection attack)
Communication Security
Almost every modern embedded device talks to something else — a phone, a gateway, the cloud, another ECU on a CAN bus. I treat every one of those channels as untrusted by default:
| Protocol | Typical Security Mechanism |
|---|---|
| Wi-Fi (MQTT/HTTPS) | TLS 1.2/1.3 with mutual authentication |
| BLE | LE Secure Connections pairing, AES-CCM link encryption |
| LoRaWAN | AES-128 network and application session keys |
| CAN bus (automotive) | CAN-FD with message authentication codes (since raw CAN has no encryption) |
| Zigbee | AES-128 network-layer encryption |
A very common real-world mistake I still see is a device that encrypts traffic to the cloud with TLS but leaves a local debug UART wide open, or trusts anything on the local CAN bus with zero authentication — which is precisely how researchers famously took remote control of a Jeep Cherokee in 2015 through the infotainment system’s connection to the CAN bus.
Real-World Applications Where Security Is Non-Negotiable
- Medical devices — insulin pumps and pacemakers with wireless configuration interfaces must resist both eavesdropping and command injection
- Automotive ECUs — secure boot and CAN message authentication prevent malicious ECU firmware and spoofed commands
- Smart meters — must resist tampering that could allow energy theft or false billing data injection
- Industrial control systems (ICS/SCADA) — Stuxnet demonstrated how a compromised PLC firmware update can cause physical destruction
- Consumer IoT — Mirai proved that even “unimportant” devices like cameras become dangerous at scale when compromised
Performance and Reliability Trade-offs
Security always costs something — cycles, RAM, flash, power, or latency. A hardware AES engine can encrypt at near-zero CPU overhead, but a software AES implementation on a small Cortex-M0 can eat noticeable CPU time and battery life. I usually budget security overhead as part of the system requirements from the start rather than trying to squeeze it in after the performance budget is already spent. Reliability and security also intersect directly: a watchdog that resets a hung device is as much a security control (against certain fault-injection or DoS attacks) as it is a reliability feature.
Debugging Without Breaking Security
One practical tension I run into constantly: I need JTAG/SWD access during development, but that same port is the number one attack vector in the field. My usual approach:
- Keep debug ports fully open during development builds
- Enable RDP (readout protection) level 2 or equivalent on production builds, permanently disabling debug access
- Use a separate, authenticated debug unlock mechanism (challenge-response) for RMA/failure analysis units only
A Professional Embedded Security Development Workflow
Over the years I’ve settled into a fairly consistent process for building security into a product rather than bolting it on afterward:
- Threat modeling first. Before writing any code, I list out the assets worth protecting (firmware IP, encryption keys, user data, physical safety) and the realistic attackers (a curious hobbyist with a debugger, a competitor trying to clone the product, a nation-state actor targeting critical infrastructure). The threat model drives every subsequent decision — a smart light bulb and an insulin pump have wildly different security budgets.
- Silicon selection. I choose an MCU/SoC based on the threat model’s requirements: does it need a TRNG, hardware crypto accelerator, secure element, or TrustZone-style isolation? Retrofitting these later usually means a full hardware respin.
- Secure boot chain design. I define the chain of trust from the very first instruction the CPU executes, choosing signature algorithms (ECDSA-P256 is common for its balance of security and small key/signature size on constrained devices) and where public keys and hashes are stored (OTP fuses ideally).
- Secure key provisioning. Keys should never be hardcoded identically across an entire product line — I use per-device unique keys generated and injected during manufacturing test, often via a hardware security module (HSM) at the factory, so a single leaked key doesn’t compromise every unit ever shipped.
- Static analysis and code review. Every pull request touching security-relevant code (bootloader, crypto, parsing of external input) gets extra scrutiny, and I run static analyzers (Cppcheck, PVS-Studio, or vendor-specific tools) as part of CI specifically configured to flag buffer overflows and integer overflow patterns.
- Penetration testing before launch. Where budget allows, I bring in a third party to actively attack the device — glitching the power rail during boot, probing debug pins, fuzzing the communication protocol — before it ships, since internal teams tend to test the paths they already know are safe.
- Incident response planning. I make sure there’s a signed OTA update pathway ready before launch, not built reactively after a vulnerability is disclosed, because the ability to patch quickly is itself a core security control.
Debugging and Testing Security Features
Testing security is fundamentally different from testing functional correctness — a feature “works” when it behaves correctly for valid input, but a security control “works” when it correctly rejects invalid, malformed, or malicious input too. My test suite for a secure boot implementation, for example, always includes deliberately corrupting the signature, truncating the firmware image, and replaying an old (rolled-back) firmware version, verifying the bootloader refuses all three. For communication security, I fuzz-test the parsing code that handles incoming protocol messages, since parser bugs on data coming from a network are historically one of the most common sources of remote exploits in embedded devices.
Common Security Mistakes I See in Embedded Projects
- Reusing the same signing key or device key across an entire product line instead of provisioning unique keys per unit
- Leaving JTAG/SWD fully open in production firmware, treating it as a “we’ll disable it later” task that never gets prioritized
- Rolling custom, unreviewed cryptography instead of using well-vetted libraries (mbedTLS, wolfSSL) — cryptography is one of the few areas where “not invented here” is actively dangerous
- Storing secrets in firmware source code or version control, where they leak the moment the repository is exposed
- Trusting all data received over a communication interface without validating length, format, or authenticity, opening the door to buffer overflows and injection attacks
- No update mechanism at all, meaning any vulnerability discovered after launch can never be fixed on already-deployed units
Security Standards and Compliance Frameworks
Depending on the industry, embedded security isn’t just good practice — it’s a regulatory requirement, and I always check which frameworks apply early in a project since they influence hardware selection and architecture:
- IEC 62443 — industrial automation and control systems security
- ISO/SAE 21434 — automotive cybersecurity engineering, now a prerequisite for many OEM contracts
- FDA premarket cybersecurity guidance — medical device software and firmware
- ETSI EN 303 645 — baseline requirements for consumer IoT security (no default/weak passwords, secure update mechanisms, vulnerability disclosure policy)
- PSA Certified (Arm) — a certification scheme covering root of trust, secure boot, and lifecycle management for IoT silicon and platforms
Building toward one of these frameworks from the start of a project is far cheaper than retrofitting compliance after a product has already shipped, since many requirements (like a hardware root of trust or secure key storage) fundamentally depend on silicon choices made at the very beginning of the design.
The Ongoing Nature of Embedded Security
Security isn’t a milestone I hit once and move past — it’s a lifecycle. Every embedded product I’ve shipped with network connectivity has needed a plan for what happens after launch: monitoring for newly disclosed vulnerabilities in third-party libraries (a TLS stack or JSON parser used in the firmware, for instance), a responsible disclosure channel for external researchers, and a tested OTA update pipeline that can actually reach deployed devices in the field. A device that was secure on its ship date but has no path to receiving a fix six months later, when a vulnerability in a bundled library is disclosed, isn’t meaningfully more secure than one that never had these protections at all.
Frequently Asked Questions
Is embedded security only necessary for internet-connected devices? No. Physical attacks (JTAG dumping, side-channel analysis) don’t require network connectivity at all. Offline devices like access-control keypads and standalone medical devices still need secure boot and tamper protection.
Can a low-cost 8-bit microcontroller ever be truly secure? It can be reasonably secure for its threat model, but most 8-bit MCUs lack hardware crypto accelerators, TrustZone, or secure key storage, so they’re generally unsuitable for high-value targets like payment or medical applications.
What’s the difference between secure boot and encrypted firmware? Secure boot verifies the firmware’s authenticity and integrity (it hasn’t been tampered with and comes from a trusted source). Encryption protects confidentiality (an attacker can’t read the firmware). You typically want both.
How often should embedded devices receive security updates? As often as new vulnerabilities are discovered in the libraries and protocol stacks they use — which is why OTA update capability is now considered a security requirement, not a convenience feature.
Summary
Security in embedded systems isn’t a checkbox — it’s an architectural discipline that starts at the silicon level and runs all the way through firmware design and network communication. A hardware root of trust, a verified secure boot chain, encrypted storage and communication, and disciplined firmware engineering practices work together to protect devices that, unlike a laptop, often can’t simply be reformatted after a breach. Given how deeply embedded systems are now woven into cars, medical devices, and critical infrastructure, I’ve come to see security not as an add-on but as a core engineering requirement equal in importance to timing and power.
