Write security requirements the team can actually test

"The system shall be secure against unauthorised access" cannot be implemented, reviewed or verified. Security requirements become useful when each one names an actor, a condition and an observable result.

Categories: Security engineering Series: Secure by design

Why most security requirements cannot be implemented

Open a typical product security requirements document and you will find statements like “the application shall enforce least privilege”, “sensitive data shall be protected in transit and at rest”, and “the system shall log security-relevant events”. Each is defensible as an intention and useless as a requirement.

An engineer cannot implement them, because they do not say what to build. A reviewer cannot reject an implementation, because any implementation is arguably compliant. A tester cannot fail them, because there is no stated condition under which the system misbehaves. And a release decision cannot rest on them, because “we enforce least privilege” is a claim about intent rather than an observation about the software.

The fix is not more requirements. It is a different shape for each one.

Give every requirement an actor, a condition and a result

A testable security requirement names who is acting, what state the system is in, and what observably happens. Compare:

Untestable Testable
Sensitive data shall be protected in transit The service shall reject any connection that does not negotiate TLS 1.2 or higher, and shall not present a certificate for any hostname other than its own
The application shall enforce least privilege A user with the analyst role requesting POST /exports shall receive 403, and no export job shall be created
Security events shall be logged A failed authentication shall emit a record containing timestamp, source address, account identifier and outcome, and shall not contain the submitted credential
Passwords shall be stored securely Credentials shall be stored using a memory-hard function with parameters recorded in configuration, and the store shall contain no reversible representation

The right-hand column is longer, and that is the point: the length is the specification. Each statement now has a failing case, which means it can be automated, reviewed in a pull request, and reported on at release.

The OWASP Application Security Verification Standard is the most practical starting library for this, because its entries are already written as verifiable statements at three levels of rigour. Take the level appropriate to the product’s risk, delete what does not apply, and add what your threat model found. Do not adopt the whole standard as a checklist—an unread requirement set is worse than a short one, because it creates the appearance of coverage.

Write the negative case first

Engineers naturally implement the positive path: the authorised user can do the thing. Security lives in the negative path, and the negative path is the part nobody specified.

So write it first. For each capability, state what must not happen:

  • a user of tenant A requesting a tenant B identifier receives 404, and no record of tenant B appears in the response or the error;
  • a request whose signature does not validate is rejected before its body is parsed;
  • an expired session cannot be refreshed;
  • a file upload with a mismatched declared type is rejected rather than sniffed and accepted;
  • a webhook replayed after its timestamp window is discarded.

Negative cases have a practical advantage: they are stable. The positive path changes with every feature iteration, while “another tenant’s data is never reachable” survives redesign and therefore stays in the regression suite for years. OWASP’s proactive controls describe the same emphasis from the design side—define the control once, then enforce it centrally rather than restating it per endpoint.

Attach requirements to features, not to a policy document

A security requirement in a separate document is read during the review that produced it and rarely again. The same requirement written as an acceptance criterion on the story that introduces the endpoint is read by whoever implements it, whoever reviews it, and whoever later changes it.

This is what the design and implementation practices in NIST’s Secure Software Development Framework describe when they place requirement definition inside the development process rather than beside it. Practically, that means:

  • product-wide requirements live in a short baseline that applies to every service, enforced by shared libraries and platform configuration where possible;
  • feature-specific requirements live on the feature, expressed as acceptance criteria;
  • a requirement that cannot be enforced centrally and cannot be attached to a feature is usually a policy aspiration, and should be recorded as such rather than as a requirement.

The baseline matters more than teams expect. If authorisation, input validation, output encoding, session handling and secret retrieval are each solved once in a shared component, most per-feature security requirements reduce to “uses the platform mechanism”, which a reviewer can check in one place.

Decide what verification each requirement gets

A requirement without an assigned verification method is an assertion. Assign one explicitly, and accept that the methods differ in strength:

Verification Suits Weakness
Automated test in the pipeline Deterministic negative cases, authorisation matrices Only covers what was imagined
Static or dependency analysis Weakness classes, known vulnerable components Noisy; does not understand intent
Code review against a checklist Design conformance, correct use of platform controls Depends on reviewer attention
Targeted manual testing Business-logic abuse, chained conditions Not repeatable per release
Design argument only Properties no test can observe Unverified; record as such

The last row is legitimate and should be visible. Some requirements—“keys are generated inside the hardware module and never exported”—are established by design and configuration rather than by a test the application can run. Recording that honestly is better than writing a test that passes for the wrong reason.

Where a product is subject to a standard such as IEC 62443-4-1, the verification column is also what an assessor asks for. Deciding it while writing the requirement is considerably cheaper than reconstructing it during an audit.

Requirements that carry an obligation

For products placed on some markets, a subset of security requirements is no longer a choice. The EU Cyber Resilience Act sets essential requirements for products with digital elements, including secure-by-default configuration, vulnerability handling and the provision of security updates for a defined support period.

These obligations are worth writing in the same testable shape as the rest, because that is the only form in which an engineering team can satisfy them. “Provides security updates” becomes a set of statements about update signing, update delivery, rollback behaviour and the duration for which the mechanism is supported—each of which is a design decision with a verification method.

The general test still applies. Read each requirement and ask: what would a failing system look like, and who would notice? If there is no answer, the sentence is a value, not a requirement, and it belongs somewhere other than the engineering backlog.

Get the agent release checklist

Working through this decision?

Turn security intent into requirements, acceptance criteria and verified implementation.

A little more clarity

Questions worth asking.

Start with the questions that shape a better decision.

Where do security requirements come from?

Three sources, in this order: the threat model for this product, the obligations that apply to it, and a baseline standard such as the OWASP Application Security Verification Standard for the classes of weakness every product of its type shares.

Should security requirements live in the backlog or a separate document?

In the backlog, attached to the features they constrain. A separate security document is read once; an acceptance criterion on a story is read every time the story is picked up.

How many requirements are enough?

Fewer than most teams write. A short set that is verified on every release is worth more than a long set that is asserted once, because only the verified set tells you anything on release day.