Security by Design — Part 1 of 3
There is a moment in almost every technology project when a seemingly innocent decision becomes expensive to undo. Perhaps the team chooses how users will sign in. Perhaps an application is given direct access to a database because it is convenient. Or perhaps a new integration is designed around a broad service account that will be restricted later.
None of these decisions looks like a vulnerability when it is made. They look like progress.
That is why I think Security by Design is fundamentally about when we ask the difficult questions. It is not a request to add more gates. It is an invitation to make better decisions while those decisions are still affordable to change.
Start with what matters
Before choosing controls, I want to understand the system: what it does, whose data it handles, who may act on that data, and where the responsibilities change.
Imagine an appointment service. A patient signs in successfully. Does that mean they can see any appointment identifier that happens to be in a URL? Of course not. Authentication tells us who they are. Authorization still has to decide whether that person may access that specific appointment.
This distinction is easy to write in a policy and surprisingly easy to lose between a browser, an API and a database. The earlier we make it explicit, the less likely it is to become an emergency fix.
A design conversation is a loop, not a single approval at the end of delivery.
Six principles worth carrying into every design
1. Make trust explicit
Draw the people, services, data stores and the connections between them. Mark where one level of trust becomes another. An API call from a browser is not the same as a call between managed services. A system that knows a user's identity does not automatically know what that user is allowed to do.
2. Give each component only the authority it needs
Least privilege is not just a role matrix. It applies to service identities, deployment pipelines, database permissions and the scope of tokens. If a background worker only reads one dataset, why should it have write access to the entire database?
3. Make the safe choice the default choice
Secure-by-default means new systems begin with useful protection already in place: restricted access, protected secrets, encryption where appropriate and logging that avoids exposing sensitive information. Teams should not have to discover a hidden checklist just to arrive at a reasonable baseline.
4. Reduce what can be exposed
Sometimes the strongest control is simply not collecting a field, not publishing an endpoint or not retaining data longer than necessary. Less unnecessary access and less unnecessary data mean fewer things to protect and fewer things to recover after an incident.
5. Design for failure as well as success
Tokens expire. Providers become unavailable. Secrets must rotate. Requests arrive in the wrong order. A resilient design defines what happens then: fail safely, preserve essential service where appropriate, surface useful signals and support recovery without improvisation.
6. Decide what evidence will prove it works
"We use OAuth" is not evidence that access is properly authorized. "The pipeline passed" is not evidence that every high-risk path is tested. Good requirements have an observable outcome: a forbidden request is denied, a revoked credential stops working, a privileged action is logged without exposing the secret.
The shift I want teams to make
| When security arrives late | When security shapes the design |
|---|---|
| "We will restrict that API later." | Define who can call it and for which resources now. |
| "The application needs database access." | Specify the exact operations and data it needs. |
| "We will add monitoring before go-live." | Define which events matter when designing the workflow. |
| "The test team will find the risks." | Turn meaningful threats into testable requirements early. |
None of this requires turning every sprint into a security conference. Often a short conversation about a data flow is enough to avoid weeks of rework.
What this looks like in practice
For a new feature, I would begin with five questions:
- What are we trying to protect, and from which realistic mistakes or abuses?
- Where does data or authority cross a trust boundary?
- Which protections are part of the design, rather than operational promises for later?
- What will we test before releasing it?
- Who will know if those assumptions stop being true in production?
There are mature references to help. CISA's Secure by Design guidance asks technology makers to take responsibility for customer security outcomes. The NIST Secure Software Development Framework (SSDF) connects preparation, software protection, secure production and vulnerability response. OWASP SAMM offers a way to improve those practices across governance, design, implementation, verification and operations.
They are useful lenses, not substitutes for understanding the actual system in front of us.
A quieter kind of security
When this works, it often goes unnoticed. Developers get a clear path. Architects can explain the trade-offs. Operations knows what must be observed. The people using the service do not have to compensate for insecure decisions made upstream.
That is the outcome I care about: security that is present in the architecture, without becoming an obstacle to building something useful.
Next in this series: Trust Boundaries: Where the Real Questions Start.