Back to all articles
Compliance path19 August 20269 min read

Integrity, attack surface and exploitation mitigation: three points, one argument

Annex I points (2)(f), (2)(j) and (2)(k) read as one engineering sequence: make it hard to change what should not change, leave less to attack, and make a successful attack buy less.

Practical guidance, not legal advice. Annex references are drawn from Regulation (EU) 2024/2847. Check them against the official text before you rely on them.

Three points in Annex I Part I are usually assessed separately and are best designed together, because read in order they are one argument. Make it hard to change what should not change. Leave less of the product exposed. Make a successful attack buy the attacker less than it otherwise would.

Taken individually each invites a one-line answer. Taken together they are the part of the file that shows whether anyone thought about the product being attacked rather than merely used.

Point (2)(f): four nouns, and most teams answer one

The requirement is to protect the integrity of stored, transmitted or otherwise processed data, personal or other, commands, programs and configuration against any manipulation or modification not authorised by the user, and to report on corruptions.

Data, commands, programs, configuration. Four categories, and assessments overwhelmingly answer for the first.

Programs

Code integrity is the strongest of the four, and it has a well-worn answer: signatures verified before the code runs, by something the code being verified cannot influence. On a device that is a boot chain rooted in something immutable, verifying each stage before handing control to it. On installed software it is signed releases, and checksums published somewhere that does not share a trust path with the download itself, since a checksum served from the compromised mirror proves nothing.

The question to answer in the file is where the root of trust lives and who can change it. If a signature-verification routine can be disabled by an unauthenticated caller, or the trusted key can be replaced through the same update path it is supposed to protect, the chain has a loop in it.

Commands

The control plane is the part of integrity people file under access control and then never revisit. A device that accepts an unauthenticated MQTT message telling it to unlock, restart or reconfigure has an integrity problem in the terms of this point, not only an authentication problem.

Replay is the specific gap. A command that was authentic yesterday and can be captured and repeated today is a modification the user did not authorise. Nonces, sequence numbers or timestamps with a bounded window, and rejection of anything outside it.

Configuration

Configuration arrives through more paths than most teams have counted: a provisioning payload at manufacture, a remote configuration push, a file on an SD card, a QR code, a settings sync from the app. Each is a path by which something changes what the product does. Signed configuration bundles, and validation of every field on arrival rather than at the point of use.

The phrase not authorised by the user

It is worth reading that clause slowly, because the reference point is the user, not the manufacturer. A silent manufacturer-initiated change to a customer's configuration is, on the face of the text, a modification not authorised by the user. That does not make every remote management feature non-compliant. It does mean a remote configuration capability needs a story about consent and visibility, and it is a better story if the customer can see what changed.

Report on corruptions

Same shape as the reporting clause in point (2)(d), and it fails the same way. Secure boot that quietly falls back to a known-good image and says nothing has protected the device and told nobody the device is under attack. Surface it: a verification-failure counter, an event on the log channel, a status flag the operator's tooling can read, a message in the app. Silent recovery is a robustness feature. Reporting is the requirement.

Point (2)(j): attack surface is a number you should be able to state

The requirement is to design, develop and produce products to limit attack surfaces, including external interfaces. Limiting implies knowing, so the first deliverable is an inventory.

What goes in it: listening ports and the process behind each; physical interfaces including USB, UART, JTAG, SWD, SD slots and debug headers; radio interfaces and every pairing mode; bundled services and their versions; file and protocol parsers that touch untrusted input; and inter-process endpoints such as sockets, D-Bus names and named pipes.

Product shapeWhat the inventory usually turns up the first time
Connected deviceA debug HTTP server on 8080 that was meant for the factory. adb or dropbear left in the production image. A BLE pairing mode that never times out. mDNS advertising the model and firmware version to the local network.
Embedded firmwareA serial console at 115200 that drops to a root shell. Unblown fuses, so JTAG is live on shipped units. A bootloader that accepts an image from TFTP with no authentication.
Installed softwareA metrics endpoint bound to 0.0.0.0. A full language runtime and package manager in a container image that only needs one static binary. Sample applications left in the web root. An administrative port documented as internal but not bound to loopback.

The exercise that produces this is cheap and nobody does it. Scan a real production unit from the network it will actually live on, not from the bench. Run ss or lsof against a running instance built from the release artefact. Photograph the production board and write down what every header is for. Compare your container image against a minimal base and account for the difference.

There is a real tension here, and pretending otherwise produces a file nobody believes. Minimising attack surface fights with diagnosing failures in the field. The workable resolution is not to remove diagnostics but to gate them: authenticated, time limited, requiring physical presence, or off until explicitly enabled by the customer, with the choice recorded. An interface you decided to keep, with a control and a reason attached, reads far better than one you did not know about.

Point (2)(k): reducing what an incident costs

The requirement is to design, develop and produce products to reduce the impact of an incident using appropriate exploitation mitigation mechanisms and techniques. It assumes something will go wrong and asks what happens next.

For anything in C or C++ there is a conventional set, and its absence is noticed: address space layout randomisation, non-executable memory, stack protection, full RELRO with immediate binding, fortified standard library calls, and control-flow integrity where the toolchain offers it. These are build settings, which makes them among the cheapest things in this entire article to fix and among the most common to find missing, usually because a vendor SDK's default makefile was never revisited.

On small microcontrollers, be honest about what the part gives you. Some MCUs have an MPU and privilege levels you can use; some have neither and no memory management unit at all. Where the hardware cannot support the standard mitigations, say so and describe what you did instead, whether that is a memory-safe language for the parsing code, static analysis with a strict rule set, or moving untrusted input handling to a component that is isolated by other means.

Language choice belongs in this answer. A network-facing service written in Rust or Go has a materially different exposure to memory-corruption exploitation than the same service in C, and the file should say which and not be coy about it. That is not a claim of security; it is a claim about one class of bug, and it is a claim you can support.

Beyond memory safety, the techniques that reduce blast radius are mostly about privilege and separation.

  • Do not run as root. On a device this is the single highest-value change available to most products, and it is usually two days of work.
  • Drop privileges after binding the port, and separate the component that parses untrusted input from the component that holds the secrets.
  • Use what the platform provides: seccomp, AppArmor or SELinux on Linux, a read-only root filesystem, a container that is not privileged and does not mount the host socket.
  • Scope credentials. A service account with access to one bucket rather than the account, a per-tenant key rather than a shared one, a device certificate that authorises that device and nothing else.
  • Segment the fleet. A compromised unit that can reach every other unit turns one incident into a recall.

Verifying rather than asserting

Most of this is mechanically checkable, which is unusual among the Annex I points and worth taking advantage of.

  • Run checksec or an equivalent against the binaries in the release artefact and keep the output. If a hardening flag is off, this is where you find out.
  • Add a build-time assertion that the hardening flags are present, so a toolchain upgrade cannot quietly remove them.
  • Test that a modified image does not boot, and that a correctly signed but older image is refused where you implemented rollback protection.
  • Script the port scan of the release image in continuous integration and fail the build when the open ports do not match the declared interface inventory. This one test keeps the inventory honest for years.

What an assessor will expect

  • The interface inventory, with a purpose and a control for every entry, and a date.
  • The boot and verification chain described end to end, including where the root of trust lives and who can change it.
  • Build hardening settings, with evidence they are present in the artefact rather than in a documented intention.
  • Test results showing that tampering is detected and reported, not only that it is survived.
  • The reasoning where a mitigation was not available, particularly on constrained hardware, with what you did instead.

Annex VII part 2 is where the design and development description lives, part 3 is where you connect these measures to the risks that justified them, and part 6 is where the test reports go.