Filter concepts by levelShowing all levels.

System Design · Security Architecture

Security Architecture

The design-time mindset, the operational controls that keep it true over time, and the risk patterns both exist to prevent

  • Threat modelingper feature, not a one-time document
  • Least privilegehumans and services both
  • Secrets managementnever in source control
  • 9 common risksinjection-family vs access-family

1.Security architecture principles: threat modeling to secure defaults

Threat modeling is the exercise of asking, for a specific system, who would attack it, how, and what they would gain — done before or during design, not after an incident. Attack surface is everything an attacker could touch: every endpoint, input field, dependency and open port, and a smaller surface is easier to defend than a larger one. Least privilege means every user, service and process gets only the access it needs to do its job, nothing more. Defense in depth means stacking multiple independent security controls so one failure does not mean total compromise. Secure defaults means the out-of-the-box configuration is the safe one, so a team that changes nothing is still reasonably protected.

Key features

  • Threat modeling asks three questions for a specific system: what are we building, what can go wrong, and what will we do about it — frameworks like STRIDE (spoofing, tampering, repudiation, information disclosure, denial of service, elevation of privilege) structure the second question
  • Attack surface grows with every new endpoint, third-party dependency, open port and input field — reducing it (removing an unused admin route, an unpinned dependency) is often cheaper than defending it
  • Least privilege applies to humans and to services alike — a service account that only ever reads one table should not hold write access to the whole database, since a compromised credential then grants only what that credential actually needed
  • Defense in depth assumes any single control can fail — a WAF, input validation, parameterized queries and least-privilege database access are four independent layers against SQL injection, not four redundant copies of the same one
  • Secure defaults shift the burden from "the team must remember to configure this safely" to "the team must deliberately choose to weaken it" — a new S3 bucket that defaults to private is secure by default; one that defaults to public is not
Defense in depth against one threat (a leaked database credential)

Network

database not reachable from the public internet

Identity

least-privilege, per-service credential

Application

parameterized queries, input validation

Data

sensitive columns encrypted at rest

Detection

audit logs alert on anomalous query patterns

  1. Network — database not reachable from the public internet
  2. Identity — least-privilege, per-service credential
  3. Application — parameterized queries, input validation
  4. Data — sensitive columns encrypted at rest
  5. Detection — audit logs alert on anomalous query patterns

Command

text
// Least privilege applied to a database credential
// Bad:  one shared "app_user" role, superuser-adjacent
// Good: one role per service, scoped to what it touches

GRANT SELECT, INSERT ON orders TO orders_service;
GRANT SELECT ON orders TO reporting_service;
-- reporting_service can never write, even if
-- its credential leaks

Threat model per feature, not once; shrink the attack surface before defending it; grant least privilege to humans and services alike; stack independent layers (defense in depth) since any one control can fail; ship secure by default so an unconfigured system is still reasonably safe.

2.Secrets management, key rotation and audit logs

Secrets management is storing passwords, API keys and certificates in a dedicated system built for it (a secrets manager or vault), never in source code or plain config files. Encryption protects data in transit and at rest and is one control among several here — it gets a full section of its own next. Key rotation is periodically replacing encryption keys and credentials so a leaked one has a limited useful lifetime. Audit logs record who did what and when, so a security incident can be investigated after the fact instead of remaining a mystery.

Key features

  • A secret committed to source control stays compromised even after deletion — it remains in git history unless the repository history itself is rewritten and the credential is also rotated, so the fix is always rotate-then-remove, not remove alone
  • A secrets manager (e.g. a vault product or cloud KMS-backed store) centralizes access control and audit logging for every secret, and lets an application fetch a secret at runtime instead of it living in a config file or environment baked into an image
  • Key rotation limits the blast radius of a leaked key to the window before rotation — a key that never rotates gives an attacker who obtains it unlimited time to use it undetected
  • Rotating a key safely needs a brief overlap window where both the old and new key validate, so requests in flight during the swap do not fail — an instant hard cutover breaks any request signed just before the switch
  • Audit logs must be tamper-evident and stored separately from the systems they observe — logs an attacker can edit or delete after compromising a host are not useful evidence of what that attacker did
The lifecycle of one secret

Store it in a secrets manager

Never in source control or a plain config file. A committed secret stays compromised after deletion — it is still in git history, so the fix is rotate first, then remove.

Fetch it at runtime

The application reads the current value when it runs, instead of holding a long-lived copy baked into an image or an environment.

Rotate on a schedule

With an overlap window where the old and new key both validate. An instant hard cutover fails every request signed just before the swap.

Audit every access

Tamper-evident, and stored somewhere other than the systems it observes. Logs an attacker can edit after taking a host are not evidence of anything.

  1. Store it in a secrets manager — Never in source control or a plain config file. A committed secret stays compromised after deletion — it is still in git history, so the fix is rotate first, then remove.
  2. Fetch it at runtime — The application reads the current value when it runs, instead of holding a long-lived copy baked into an image or an environment.
  3. Rotate on a schedule — With an overlap window where the old and new key both validate. An instant hard cutover fails every request signed just before the swap.
  4. Audit every access — Tamper-evident, and stored somewhere other than the systems it observes. Logs an attacker can edit after taking a host are not evidence of anything.

Command

text
// Fetching a secret at runtime instead of
// hardcoding it or reading it from a plain .env
// committed to the repo:

db_password = secrets_manager.get_secret("prod/db/password")
// rotated on a schedule by the secrets manager;
// the application always fetches the current value,
// never stores its own long-lived copy

Secrets live in a dedicated secrets manager, never in source control or plain config. Encryption is one control among several here (full treatment next section). Rotate keys and credentials on a schedule with an overlap window so a leak has a limited lifetime. Audit logs must be tamper-evident and stored separately from what they observe, or they cannot be trusted after a compromise.

3.Common vulnerability classes: injection, access control and leakage

These nine risks account for most real-world application breaches, and every one of them has a well-understood fix. Some are injection risks (SQL injection, XSS, command injection, insecure deserialization), where untrusted input is executed as code instead of treated as data. Others are access risks (broken access control, CSRF, SSRF, internal endpoint abuse), where a request reaches something it should not be able to reach. Credential leakage is the exposure of a secret that then unlocks any of the above. Knowing the pattern behind each one — not memorizing exploit syntax — is what lets you recognize the same shape in a new codebase.

Key features

  • SQL injection and command injection share one root cause: untrusted input concatenated into a command string instead of passed as a separate, typed parameter — parameterized queries and argument arrays (never shell string-building) are the fix for both
  • XSS (cross-site scripting) runs an attacker's script in another user's browser because untrusted input was rendered as HTML/JS instead of escaped text — output encoding and a Content-Security-Policy are the standard defenses
  • CSRF (cross-site request forgery) uses a victim's already-authenticated session to submit a forged request from another origin — anti-CSRF tokens and the SameSite cookie attribute close it
  • SSRF (server-side request forgery) tricks a server into making a request on the attacker's behalf, often to internal-only infrastructure (a cloud metadata endpoint) unreachable from outside — allowlisting outbound destinations and blocking internal IP ranges are the fix
  • Broken access control is checking "is this caller logged in" without checking "is this caller allowed to touch this specific object" — the most common real-world finding, since it requires a positive per-object check rather than a single global gate
  • Insecure deserialization runs attacker-supplied data through a deserializer capable of instantiating arbitrary objects or executing code — safe formats (JSON with a fixed schema) and never deserializing untrusted data with a general-purpose deserializer are the fix

Nine common risks: the pattern and the fix

Nine common risks: the pattern and the fix
RiskPatternPrimary fix
SQL injectionUntrusted input concatenated into a SQL stringParameterized queries / prepared statements
XSSUntrusted input rendered as HTML/JS without escapingOutput encoding + Content-Security-Policy
CSRFForged request riding an existing authenticated sessionAnti-CSRF tokens + SameSite cookies
SSRFServer tricked into requesting an attacker-chosen URLAllowlist outbound destinations; block internal IP ranges
Broken access controlChecks "logged in" but not "allowed to touch this object"Per-object authorization check on every request
Command injectionUntrusted input concatenated into a shell commandArgument arrays, never shell string-building
Insecure deserializationUntrusted data fed to a general-purpose deserializerFixed-schema formats; never deserialize untrusted data generically
Credential leakageSecrets in source control, logs, errors, or client codeSecrets manager + rotation (see prior concept)
Internal endpoint abuseEndpoint restricted only by being undocumentedReal authentication + network-level restriction
Two families of risk, one shared cause

Injection-family

  • SQL injection, command injection, XSS, insecure deserialization
  • Untrusted input executed as code instead of treated as data

Access-family

  • Broken access control, CSRF, SSRF, internal endpoint abuse, credential leakage
  • A request or credential reaches something it should not
  • Injection-family
    • SQL injection, command injection, XSS, insecure deserialization
    • Untrusted input executed as code instead of treated as data
  • Access-family
    • Broken access control, CSRF, SSRF, internal endpoint abuse, credential leakage
    • A request or credential reaches something it should not

Command

sql
-- SQL injection: the vulnerable pattern
-- (string concatenation, not parameters)
-- query = "SELECT * FROM users WHERE name = '" + input + "'"

-- Fixed: parameterized query
SELECT * FROM users WHERE name = $1;
-- input is passed as a bound parameter, never
-- concatenated into the query text

Injection-family risks (SQL injection, command injection, XSS, insecure deserialization) run untrusted input as code instead of data — parameterize, encode, use fixed schemas. Access-family risks (broken access control, CSRF, SSRF, internal endpoint abuse, credential leakage) let a request or secret reach something it should not — authorize per object, verify origin, restrict by network and identity, never by obscurity.

Remember

  • Threat model per feature as it ships — a threat model written once at launch describes a system that no longer exists a year later.
  • Defense in depth assumes any single control fails — a WAF and nothing else means one bypass is a complete breach, not a contained one.
  • Secrets live in a dedicated secrets manager and rotate on a schedule — a leaked credential that never expires gives an attacker unlimited time.
  • Broken access control is the most common real-world finding: checking "logged in" is not the same as checking "allowed to touch this object."

Pro tip

  • Fix a vulnerability class at the shared code path (templating engine, query builder), not just the one field a scanner happened to flag.
  • An "internal" endpoint restricted only by being undocumented is not restricted at all — it needs real network isolation and authentication.

Next up

  • Encryption
  • TLS and encryption at rest
  • Key management (KMS/HSM)

Sources

  • OWASP Top Ten project — owasp.org/www-project-top-ten
  • Checked 2026-08-25

Spotted a mistake on this page? Tell us.