Secure Software Architecture

Security by Design: Why Secure Software Starts Before Development

Security by design means deciding trust boundaries, identities, privileges, data and recovery before implementation makes them expensive to change. Tools can verify parts of the design, but they cannot supply the missing decisions.

Identity, data, secrets and delivery controls designed into a secure software blueprint
Security architecture defines what the application may trust before features create accidental boundaries.

Security requirements are architecture inputs

Teams often postpone security until a penetration test near launch. At that point, changing a tenant boundary, identity model or data flow may require redesign rather than correction. A late test is valuable evidence, but it cannot substitute for early decisions.

Begin with assets, actors and consequences. What data or business capability matters? Who may perform which action? Which systems cross organisational or network boundaries? What should happen when an identity provider, queue or database is unavailable?

Security by design is not maximum control everywhere. It is proportionate control placed where failure matters, with a recovery model the organisation can operate.

Threat modelling makes assumptions discussable

A threat model maps trust boundaries, entry points, valuable assets and plausible misuse. It should include support tools, administration, imports, webhooks and background jobs, not only the public user interface.

The useful output is a set of decisions and tests. For example: webhook messages require signature verification and replay protection; exported files must not expose another tenant; privileged support access needs approval and durable audit.

Update the model when architecture changes. A diagram created for procurement and never revisited is not a control.

Secure delivery chain from threat modelling through identity, secrets, build checks and monitoring
Security controls form a delivery chain whose weakest unowned boundary still matters.

Authentication proves identity; authorisation protects resources

Authentication answers who the caller is. Authorisation decides whether that identity may perform this action on this resource. A valid token is not permission to read any record whose identifier the caller can guess.

Use established identity providers and standards rather than storing passwords in bespoke code. Microsoft Entra ID and Entra External ID can provide managed identity capabilities in appropriate workforce and customer scenarios. The application still owns resource-level rules, tenant separation and meaningful access-denied behaviour.

Role-based access control is useful for coarse business responsibilities, but roles alone become unwieldy when rules depend on ownership, organisation, state or delegated authority. Combine roles with policy and resource context. Test both permitted and denied paths.

Least privilege limits the consequence of a defect

Give users, applications and deployment systems only the access needed for their role. Separate runtime identity from deployment identity. A web application that can alter its own infrastructure turns one application compromise into a broader platform compromise.

Use short-lived workload identity where the platform supports it. In Azure, managed identities allow applications to request tokens without storing a client secret. They remove one secret but do not make permissions correct; over-privileged managed identity is still over-privileged.

Review privilege over time. Temporary diagnostic access and abandoned automation identities often outlive their purpose.

Secrets management is a lifecycle

Azure Key Vault can centralise secrets, keys and certificates with governed access. It does not find credentials copied into source, logs or developer machines. Prevent creation where possible, inventory the remainder and design rotation before an incident.

A request-time vault lookup creates an availability dependency. Cache carefully where appropriate, bound the lifetime and decide what happens when the vault is unavailable. Rotation needs overlap or coordinated restart so new and old components do not disagree.

Never log tokens or secret values. Redaction must apply to structured fields, exception messages and outbound request diagnostics, not only formatted strings.

Secure API design assumes hostile input and uncertain networks

Validate structure and business rules at the boundary, but do not confuse validation with authorisation. Enforce size limits and stream large bodies to avoid turning a valid endpoint into a memory-exhaustion route.

Outbound calls are also a trust boundary. Restrict destinations where URLs can be influenced, protect metadata endpoints and validate redirect behaviour to reduce server-side request forgery risk. A successful DNS lookup does not make a destination trusted.

Retries interact with idempotency. After a timeout, a remote write may have succeeded even though the client saw no response. Safe APIs need idempotency keys, reconciliation or business-specific duplicate protection.

Protect data in context

TLS protects data in transit between defined endpoints. Storage encryption protects media or database files. Neither prevents an authorised but incorrect query from returning another customer's data.

Classify data, minimise collection and design retention and deletion. Encryption changes search, key rotation, backup and recovery. If keys disappear before retained backups expire, recovery may fail; if old keys never retire, deletion promises may be undermined.

Logs, exports, caches and analytics are additional copies. The data-flow model must include them rather than stopping at the primary database.

Build pipelines are part of the security boundary

Automated pipelines can perform repeatable builds, tests, dependency checks and deployment approval. They also hold powerful identities and can distribute compromised artefacts at scale. The NCSC's build-pipeline guidance emphasises protecting the pipeline and isolating builds. Protect branch policies, runners, package sources, signing material and environment approvals.

Pin and restore dependencies predictably. A build that silently takes a different package from the same version range is hard to reproduce during investigation. Keep generated artefacts immutable between tested and production stages.

Scanning is a gate with judgement, not an oracle. Define how findings are triaged, how exceptions expire and who can bypass a control during an emergency.

Secure defaults reduce reliance on memory

  • Deny access unless a policy grants it.
  • Use secure, HTTP-only cookie settings appropriate to the flow.
  • Disable unused endpoints, methods and diagnostic interfaces.
  • Keep detailed errors out of user responses while preserving correlated server diagnostics.
  • Apply restrictive cross-origin and content policies based on actual consumers.
  • Use named identities and auditable privileged actions.
  • Make new tenants and users start with minimum access.

A secure default must remain usable. If a control makes ordinary work impossible, people create side channels. Observe the workflow and design safe routes for legitimate exceptional work.

Design recovery while the system is still on paper

Decide how to revoke sessions, rotate compromised credentials, restore data and disable a risky integration. Include incident visibility: which telemetry shows a failed authorisation pattern, unusual export or repeated signature failure?

Recovery tests reveal hidden dependencies in identity configuration, certificates and external services. Backup success is not recovery evidence until a representative restore meets agreed objectives.

Operational ownership belongs in architecture because every control creates maintenance: alerts, access reviews, patching, key rotation and false positives.

A pre-development security checklist

  • Assets, users, administrators and trust boundaries are identified.
  • Authentication uses a maintained identity platform.
  • Authorisation is expressed at resource and tenant boundaries.
  • Human and workload privileges are minimised and reviewable.
  • Data flows cover logs, exports, backups and retention.
  • Secrets have storage, rotation and outage behaviour.
  • APIs define validation, size, replay and failure semantics.
  • The pipeline protects source, packages, artefacts and deployment identities.
  • Monitoring and incident decisions have owners.
  • Recovery and dependency failure are testable.

Test abuse cases as well as expected journeys

Acceptance tests prove that an authorised user can complete work. Security tests should also prove that another user, tenant or role cannot complete it. Use identifiers belonging to different principals, replay old requests, alter workflow order and attempt actions after access is revoked.

Test concurrency around approval, stock, payments and one-time actions. Two individually authorised requests can violate a business rule when processed together. Database constraints, idempotency and transactional boundaries often protect these cases better than an interface check.

Include failure behaviour. If the identity provider is unavailable, the application should not silently grant access. If audit storage fails, decide whether a privileged operation stops or continues with an alert. Availability and security can conflict, and the architecture should make the chosen trade-off explicit.

Automated checks cannot prove the whole system secure. Combine unit and integration tests with code review, dependency analysis, configuration review and proportionate independent testing. Each method observes a different failure class.

Make the secure path the easiest path

Security by design pays off when teams can implement normal features without reinventing identity, secrets, error handling or deployment controls. Architecture supplies safe paved roads and makes exceptional risk visible.

Read more about authentication and authorisation in ASP.NET Core, Key Vault and managed identity and multi-tenant isolation.

Keep design decisions alive

Record consequential choices, rejected alternatives and the evidence that would trigger review. Future teams can read code, but they cannot infer every regulatory, operational or threat assumption that shaped it. Short decision records make secure change safer.

Frequently asked questions

Does security by design guarantee secure software?

No. It creates deliberate boundaries and controls, which still need correct implementation, verification, maintenance and incident readiness.

Should we build our own authentication?

Usually not. Established identity platforms reduce bespoke credential-handling code. The application must still implement its own authorisation correctly.

Is a penetration test still useful?

Yes. It can provide independent evidence about the implemented system, but it should test a considered design rather than discover foundational decisions at the end.

Resolve expensive security decisions before implementation

If a bespoke application needs discovery or architecture with security built into its boundaries, talk to C3 Software about the delivery model.