Back to Blog
Security15 min readJun 2026

Security as a Non-Functional Requirement

Security isn't a feature you add at the end, it's a property of the whole system, baked in from the first design decision. This is the engineer's mental model: defense in depth, least privilege, secure defaults, encryption everywhere, secrets management, the shared responsibility model, and shifting left.

SecurityDefense in DepthLeast PrivilegeNFR
SB

Sri Balaji

Founder ยท TheSimplifiedTech

On this page

Security is not a feature you bolt on

Picture the classic project plan: build all the features, then "do a security pass" the week before launch. It never works. By then the architecture is set, permissions are wide open "to make it work," secrets are hardcoded, and the security pass becomes a frantic list of things that can't be fixed without rewrites. Security treated as a final checkbox is security that fails.

The senior mental shift is to treat security as a non-functional requirement, a property of the system like performance or reliability, present in every decision from day one. You don't "add" reliability at the end, and you can't add security at the end either. This article is the engineer's working model: the principles that make security a default rather than an afterthought.

Who this is for

Engineers who build things and want security to be instinctive rather than a separate team's problem. No security background needed, every principle is concrete and immediately applicable.

What 'non-functional requirement' actually means

Functional requirements are what the system does. Non-functional requirements are how well it does them, fast, available, and secure. Users notice functional features; they only notice non-functional ones when they're missing, and with security, by then it's a breach.
๐Ÿฐ Concentric castle walls, not one gateDefense in depth
๐Ÿ”‘ A key that opens only your officeLeast privilege
๐Ÿ”’ Doors that lock when they closeSecure defaults
๐Ÿ›ก๏ธ A safe inside a locked roomEncryption + secrets management
Security is a property of the whole building, not a gadget you install last.

The rest of this article is those four pictures turned into engineering practice. None of them is exotic, they're the boring, repeatable habits that separate systems that get breached from systems that don't.

Defense in depth, assume each layer will fail

A medieval castle didn't rely on one wall. It had a moat, an outer wall, an inner wall, and a keep, so breaching one defence still left you facing the next. Defense in depth applies the same logic: never rely on a single control, because any single control can fail. Assume your firewall *will* be misconfigured one day, and make sure that alone doesn't mean game over.

  • Network, private subnets, security groups, a WAF in front of public endpoints.
  • Identity, strong authentication, MFA, scoped access tokens.
  • Application, input validation, output encoding, dependency scanning.
  • Data, encryption at rest and in transit, so stolen storage is useless.
  • Monitoring, audit logs and alerts so a breach is *detected*, not discovered months later.

The test of good design: "if an attacker gets past *this* layer, what stops them next?" If the honest answer is "nothing," you have a single point of security failure, and that's exactly what defense in depth exists to eliminate.

Least privilege, the smallest possible blast radius

Least privilege means every user, service, and process gets exactly the permissions it needs to do its job, and not one more. Why it matters is simple: when (not if) a credential leaks or a service is compromised, the damage is bounded by what that identity could do. An over-permissioned service account is a breach waiting to be amplified into a catastrophe.

iam-policy.json
json
// โŒ Convenient, catastrophic. One leaked key owns everything.
{ "Effect": "Allow", "Action": "*", "Resource": "*" }

// โœ… Least privilege. This service can only read one bucket.
{
  "Effect": "Allow",
  "Action": ["s3:GetObject"],
  "Resource": "arn:aws:s3:::app-uploads/*"
}

Wildcard permissions are the #1 cloud finding

`Action: "*"` on `Resource: "*"` is the most common serious misconfiguration in cloud security audits. It's added "temporarily to unblock" and never removed. Start every policy from zero and add only what fails without it, deny by default, grant deliberately.

Secure defaults & encryption everywhere

Most people never change a default. So the safest system is one where **the default *is* the secure choice**, the database isn't publicly reachable unless you explicitly open it, the storage bucket is private until you make it public, the new user has no permissions until you grant them. Security that requires extra effort gets skipped; security that's the path of least resistance gets used.

Encryption in transit and at rest

Two flavours, both non-negotiable. In transit: every connection uses TLS, so traffic can't be read or tampered with on the wire, no plain HTTP, even internally. At rest: data on disk is encrypted, so a stolen drive or snapshot is useless without the key. Both are nearly free to enable in the cloud and there is no good reason to skip either.

Secrets management

Passwords, API keys, and tokens must never live in code or in a committed .env file, git history is forever, and a leaked key in a public repo is scanned and abused within *minutes*. Use a dedicated secrets manager (AWS Secrets Manager, Vault, your platform's secret store), inject secrets at runtime, and rotate them regularly. Treat any secret that ever touched a repo as already compromised.

Pro tip

Add a pre-commit secret scanner (gitleaks, trufflehog) to your repos today. The cheapest possible breach prevention is catching the key *before* it's committed, not after it's been exploited.

The shared responsibility model & shifting left

A dangerous assumption sinks a lot of teams: "we're in the cloud, so security is handled." The cloud provider operates the shared responsibility model, they secure the cloud *itself* (the hardware, the data centre, the hypervisor); you secure what you put *in* it (your data, your access policies, your application, your configuration). The provider gives you locks; locking the doors is on you. Most cloud breaches are customer misconfigurations, not provider failures.

Shifting left means moving security earlier in the development process, left on the timeline. Instead of a security review at the end, you scan dependencies in CI, lint infrastructure-as-code for misconfigurations, run static analysis on every pull request, and check for secrets before merge. Catching a vulnerability in code review costs minutes; catching it in production costs an incident.

Common mistakes that cost hours (or careers)

  1. Hardcoded secrets in code or `.env` files committed to git. History is permanent and public repos are scanned constantly. Use a secrets manager and a pre-commit scanner.
  2. Wildcard IAM permissions. * on actions and resources turns any single compromise into a total one. Grant the minimum and audit regularly.
  3. Relying on a single security control. One firewall, one WAF, one anything. Layer defences so failing one isn't fatal.
  4. Assuming the cloud provider secures your data. They secure the infrastructure; your config and access are *your* job. Most breaches are misconfigurations.
  5. Treating security as a launch-week checkbox. By then the architecture is fixed and the fixes are rewrites. Bake it in from the first design decision.

Where to go next

The whole article in 6 lines

  • Security is a **non-functional requirement**, a property of the whole system, present in every decision, not a final pass.
  • **Defense in depth**: never rely on one control; assume each layer can fail and put another behind it.
  • **Least privilege** bounds the blast radius of every leaked credential, deny by default, grant deliberately.
  • **Secure defaults** win because nobody changes defaults; make the safe choice the easy one.
  • Encrypt **in transit and at rest**, and keep secrets out of code with a real secrets manager.
  • Know the **shared responsibility model** (your config is your job) and **shift security left** into CI.

These principles are the foundation; identity and network design are where you apply them most. Go deeper:

Run a secret scanner over one repo and audit one IAM policy for wildcards this week. Those two habits prevent a startling share of real breaches.

Want to go deeper?

This article covers concepts taught hands-on in the Cloud Engineer and DevOps career paths, with real terminal labs, production scenarios, and structured lessons.