Skip to content
Back to Insights
Cyber-Security

Security Should Begin at Architecture, Not Deployment

Why security decisions made during architecture and implementation reduce attack surface, make verification possible, and keep later remediation manageable.

Waleed Sarfaraz, Founder & Director

6 min read

On this page
  1. Attack surface is a design output
  2. Trust boundaries decide where checks belong
  3. Authentication and authorization are different decisions
  4. Input validation is a contract
  5. Secrets need a place before they need a value
  6. Dependencies are part of the design
  7. Logging and observability are security features
  8. Why late remediation is harder
  9. What this does not replace
  10. Conclusion

Most teams agree that security matters. Fewer agree on when it should happen. In many projects it arrives late: a scan before release, a penetration test after launch, a checklist the week the product goes live. By then the important decisions have already been made, and security becomes a matter of finding problems in a shape that was never designed to be defended.

This is not an argument that architecture can make a system secure on its own. It cannot. Vulnerabilities still appear in well-designed systems, and testing late in the cycle still finds real issues. The argument is narrower: the decisions that most affect how hard a system is to attack, and how hard it is to fix, are made early. If security is not part of those decisions, it is left to compensate for them.

Attack surface is a design output

Every public route, background job, file upload, webhook, admin screen and third-party integration is a place where untrusted input can enter the system. None of these appear by accident. They are the result of design choices, and each one has a cost that is easy to overlook when the choice is made.

Asking a few questions while the design is still cheap to change tends to shrink that surface:

  • Does this endpoint need to be public, or only reachable from another service?
  • Does this feature need to accept files, or would a structured form do?
  • Does the admin interface need to live on the same origin as the public product?
  • Does this integration need write access, or only read access?

A route that never exists never needs a patch. That is the most reliable form of security there is, and it is only available at design time.

Trust boundaries decide where checks belong

A trust boundary is any point where data or control passes from something you trust less to something you trust more: from a browser to an API, from an API to a database, from a user-supplied repository to an analysis engine, from a webhook sender to an internal queue.

Drawing these boundaries explicitly during architecture makes two things clear. First, where validation, authentication and authorization have to happen. Second, which components are allowed to assume their input is already safe. When boundaries are implicit, both answers are guessed, and the guesses tend to be inconsistent across the codebase.

A useful rule is that every boundary should have one obvious owner of the check. If three layers each partly validate the same value, it is hard to know which one is actually protecting the system, and easy for a later change to remove the one that mattered.

Authentication and authorization are different decisions

Authentication answers who is making the request. Authorization answers whether that identity may perform this action on this resource. Systems often design the first carefully and leave the second to be added route by route.

That pattern produces the most common class of access-control mistakes: an endpoint that correctly checks that a user is logged in, but not that the record they are requesting belongs to them. Deciding the authorization model early — roles, ownership, tenancy, what an administrator can and cannot do — gives every route the same rules to apply, and makes it possible to test those rules in one place.

Input validation is a contract

Validation is easier to get right when it is treated as the contract of an interface rather than as defensive code sprinkled through the implementation. If an API declares that a field is an integer between 1 and 500, a typed schema can enforce that at the boundary and the rest of the code can rely on it.

const ScanRequest = z.object({
  repository: z.string().url(),
  depth: z.enum(["baseline", "intelligence", "deep"]),
});

This does not remove the need for context-specific safety. Data that is valid by schema can still be dangerous when it is placed into a SQL query, a shell command or an HTML page. Parameterised queries, argument arrays instead of shell strings, and output encoding are still required where the data is used. But a clear contract at the boundary narrows what the rest of the system has to worry about.

Secrets need a place before they need a value

Where credentials live, who can read them, how they rotate and what happens when one leaks are architecture questions. Answering them after the first key has already been committed to a repository is much harder than answering them before.

In practice this means deciding early that secrets come from the environment or a secrets manager rather than configuration files, that services receive only the credentials they need, and that tokens are scoped and time-limited wherever the provider allows it. It also means deciding that a secret found in source control is an incident to rotate, not a line to delete.

Dependencies are part of the design

Every library added to a project brings its own code, its own maintenance history and its own dependencies. Choosing a dependency is choosing to trust its authors and to track its advisories.

This does not mean avoiding libraries. It means choosing them deliberately: preferring well-maintained packages, avoiding several libraries that do the same job, pinning versions so that builds are reproducible, and knowing how the project will find out when a dependency has a known vulnerability. A system that cannot answer that last question cannot really claim to manage its dependency risk.

Logging and observability are security features

When something does go wrong, the first questions are what happened, when, and to which data. A system can only answer them if it was designed to record the right events: authentication failures, permission denials, changes to roles and configuration, and access to sensitive records.

Two cautions apply. Logs are themselves sensitive, so they must not contain passwords, tokens or unnecessary personal data. And logs that nobody reads protect nothing, so the design should include who looks at which signals, and what counts as worth investigating.

Why late remediation is harder

A vulnerability found after deployment is usually not a single bad line. It is often a consequence of a structure: an authorization check that was never centralised, a component that trusts data it should not, a boundary that was never drawn. Fixing it properly may mean changing interfaces that other code now depends on, migrating stored data, or coordinating a release across services.

Teams under that pressure often choose a narrow patch instead of the structural fix. The patch closes the reported case but leaves the pattern in place, and the same class of issue appears somewhere else later. Early design decisions do not eliminate this, but they make the structural fix smaller and the narrow patch less tempting.

What this does not replace

Designing for security does not remove the need for code review, automated scanning, dependency monitoring, testing and, where the risk justifies it, independent assessment. Those activities find the mistakes that good design still allows. What design changes is the kind of mistakes they find: fewer structural ones, more local ones, and local ones are far cheaper to fix.

Conclusion

Security at deployment asks whether the system is safe enough to release. Security at architecture asks what the system should look like so that it can be made safe, verified and repaired. The second question is harder to postpone than it looks, because the answers to it are built into everything that comes afterwards.

  • security architecture
  • application security
  • trust boundaries
Related serviceSecurity TestingPenetration testing, code review, infrastructure checks, and audits, with written authorisation first.

Related

All insights
  • Automation & AI

    Automation as Infrastructure

    Why durable automation should be designed as a system of triggers, validation, state, execution, retries, and audit — not a collection of disconnected scripts.

    4 min read