Secure Architecture and Design Principles Questions
Designing systems that are secure by construction: core design principles (least privilege, separation of duties, fail-safe and fail-secure defaults, secure-by-default, attack surface reduction, assume-breach), defense-in-depth and layered control placement, classifying controls as preventive, detective and corrective, secure design patterns such as tenant isolation and blast-radius limiting, security architecture reviews and secure-by-design checklists, and reasoning about trade-offs between security, usability, performance and delivery speed when selecting and placing controls, including build, native or buy choices and making the secure option the easy one for developers. Covers enterprise-scale reference architecture, such as placing enforcement across hybrid and multi-cloud estates and giving many teams a consistent baseline, how security requirements shape system structure, designing safeguards to degrade safely when a dependency is down or in an emergency, and testing whether layers and isolation hold. Boundary: the mechanics of identity, cryptography, networking, threat models, detection, incident response and compliance evidence are covered elsewhere.
How do you think about preventive, detective and corrective controls when you design a system? Take a customer-facing web application and show how you would balance the three, and what a dangerous gap in the mix looks like.
Sample Answer
Direct answer
Controls come in three jobs. Preventive controls stop a bad event from happening. Detective controls notice that it is happening or has happened. Corrective controls limit the damage and restore normal operation. A sound design uses all three because prevention is never perfect, so you need to see failures and be able to act on them. The dangerous gap is a mix with no working detection or no working correction: you cannot respond to what you cannot see, and seeing without a way to act only gives you better information about the loss.
Customer-facing web app, worked example: credential stuffing (attackers replaying passwords leaked elsewhere)
| Class | Control | Stack tier |
|---|---|---|
| Preventive | MFA (multi-factor authentication), rate limits, check passwords against known breached lists | Application and edge |
| Detective | Alert when failed-login volume or distinct-IP spread passes a baseline; alert on a sudden rise in password-reset emails | Logging and monitoring |
| Corrective | Force reset for affected accounts, revoke active sessions, block abusive IP ranges, notify users | Application and operations |
More examples across the stack:
- Cloud storage: a rule that blocks public buckets (preventive), an alert when a bucket policy changes (detective), an automated job that reverts the change (corrective).
- Data: least-privilege database accounts (preventive), alerts on unusually large exports (detective), restore from tested backups (corrective).
- Code: dependency checks in the build pipeline (preventive), runtime anomaly monitoring (detective), a one-step rollback (corrective).
How I balance them
Weight toward prevention where it is cheap and reliable (patching, secure defaults). Weight toward detection and correction where prevention cannot be guaranteed: an attacker holding a valid stolen password looks like a normal user, so detection and fast session revocation matter more than another input filter.
Dangerous gaps
- Preventive only: a breach can run for months unseen.
- Detective without correction: alerts fire but nobody can disable the account or restore data quickly.
- Detective without an owner: the alert goes to an unwatched mailbox at 2 a.m.
- Corrective never tested: the backup that fails on restore day.
How a tester validates each class
Preventive: try to bypass it. Detective: run the attack in a safe test and confirm the alert fired, how fast, and that a human saw it. Corrective: run a restore or revocation drill and time it.
Here is an architecture: public web servers, a single application tier, and one database, all sitting behind one firewall. Where does the failure of a single control turn into a full compromise, and what would you change first to bound the damage?
Sample Answer
Direct answer
In this design one firewall is the only boundary, so any single failure turns into full compromise at three main points (the first three rows of the table below; the last three rows are amplifiers that make any breach worse): a compromised public web server sits on the same flat network as the app and the database; the single app tier holds a database credential that is probably broad; and the firewall itself is one policy, one admin plane and one failure point. I would change the network first: split the three tiers into separate zones with deny-by-default rules between them, because it is quick and limits every later failure.
Where one failure becomes everything
| Weak point | What fails | Result |
|---|---|---|
| Flat network behind one firewall | A web server is exploited | Attacker can scan and reach the app and database directly |
| Firewall rules too broad or misconfigured | One bad rule | All tiers exposed at once, no second boundary |
| App tier holds a broad database login | App is exploited (for example SQL injection) | Attacker can read and write all data, as the app can |
| One database for all data | Any database access | Everything is in the blast radius (the amount of damage one failure can cause) |
| No egress (outbound) filtering | Any host compromised | Data can leave freely; the attacker can fetch tools |
| Logs only on the same hosts | Host compromised | Attacker deletes the evidence |
What I would change, in order
- Segment into three zones: web servers in a DMZ (demilitarized zone, a network segment for internet-facing hosts), app tier and database in separate private segments. Allow only web to app on one port and app to database on one port; everything else denied. A web-server compromise now yields a web-server compromise, not the database. Enforce the zone boundaries at separate enforcement points rather than as more rules on the one existing firewall: for example host-level or cloud security-group rules on each tier, administered from a different account or console than the edge firewall, or a second internal firewall with its own admin credentials and rule source. If one device and one admin plane enforce every boundary, a single bad rule or one stolen admin login still removes all of them.
- Shrink the database login: a dedicated account for the app with only the operations on the tables it uses, no administrative rights. This bounds what a hacked app can do.
- Restrict outbound traffic from each zone to what it needs, and ship logs off-host to a separate system.
- Then separate sensitive data into its own store and add a second control type (such as a WAF in front of the web tier).
What would change my order
If the known weakness is SQL injection in the app, I would do the database account first. If the firewall is the bigger worry (for example a poorly governed rule set), the segmentation comes first anyway, provided it is enforced at separate points (see step 1), because then it adds a second boundary.
Pitfall
Adding more rules to the same single firewall is not a second layer; the layers must fail independently.
How do you make the secure option the easy option for developers? Describe one platform or architecture decision that gives teams speed and security together, and how you decide between a guardrail and a gate.
Sample Answer
Direct answer. Make the secure path the pre-built, fastest path: a "paved road" (a supported, pre-assembled way to build and deploy that has the security controls already inside it). Then choose per control whether it is a guardrail (prevents or corrects automatically and lets people keep moving) or a gate (a stop that needs a human approval). Default to guardrails, and reserve gates for decisions that are hard to undo or very damaging.
The platform decision: a golden service template. One decision that delivers both: provide an infrastructure-as-code module (infrastructure described in files kept in a repository, so it is created and reviewed like software) and deployment pipeline template that creates a service with the baseline already wired in:
- private networking and no public storage by default;
- an identity for the service with narrowly scoped permissions;
- encryption on, with keys managed by the platform;
- logs and security events shipped to the central system;
- dependency and secret scanning in the pipeline.
A team starting from the template ships in less time than a team assembling those pieces itself, so the secure option is also the lazy option. In practice a team runs one command with a service name and language (illustrative: new-service invoices --lang python) and gets a repository, a pipeline that builds, scans and deploys, a service identity, a log destination and a private endpoint, with a working "hello world" deployed. The team skips writing, debugging and getting review for each of those pieces, which is where the time saving comes from. Policy-as-code (security rules written as automated checks) catches teams who leave the road.
Guardrail or gate? Four questions
- Reversibility. Can we undo the mistake cheaply? If yes, guardrail. A public bucket can be flipped back, though the data may already be out, so the guardrail must act before exposure.
- Blast radius. How much can one error reach? Blast radius means how much one error or compromise can reach. A change to the production identity trust policy (the rules stating who or what is allowed to take on a role in our account) reaches everything, so it gets a gate.
- Detectability. Would we notice quickly? If not, prefer a preventive guardrail.
- False-positive cost. If the rule blocks legitimate work often, teams route around it. Use warn-only until the rule is accurate.
Two changes traced through this rule (illustrative). A cross-account trust means letting a role in someone else's cloud account act inside ours. Change one: a developer adds a storage bucket with public read. The pipeline's policy-as-code check fails the pull request with the message "public storage is not allowed; use a signed link", the developer fixes it in the same pull request, and no human reviewed anything. Change two: a developer adds a role that a partner's account can assume. The check detects a trust relationship to an account outside our organization and routes the change to a named reviewer, who sees the partner, the permissions granted and the requested expiry, then approves, narrows or declines it before it can be applied.
Minimum viable controls under release pressure. When a product manager needs to ship, I hold five things: authenticated access, server-side authorization checks, no secrets in code, logging of security events, and a way to turn the feature off. Everything else can be dated follow-up. The same approach holds under cloud-migration pressure: migrate workloads into a landing zone (a pre-secured account setup) that already carries the baseline, so velocity does not require individual security reviews.
How acceptance is measured
- Share of services built from the template (adoption).
- Time from new repository to first production deploy, compared before and after.
- Number and age of exceptions, and how many policy findings are fixed automatically.
- Developer feedback: do teams choose the road when not forced?
If adoption is low, the road is too slow or too restrictive, and that is the thing to fix.
Pitfalls. Mandating the road before it is good. Gates that no one staffs, producing queues. Guardrails with no exception path, which push teams to shadow infrastructure.
Support staff need to act on customer accounts through a high-security portal. How do you design access and authentication so support stays efficient but a compromised or socially engineered support account cannot do serious damage?
Sample Answer
Direct answer. I would make the support portal give agents the narrowest standing access (permissions an agent holds all the time) that makes common tasks fast, put anything risky behind step-up checks (a fresh re-authentication, such as touching the hardware key again, demanded only for risky actions) and a second person, and assume one support account will eventually be phished. The aim is a small blast radius (the damage one compromised account can do) rather than a perfect login.
Design layers.
- Strong, phishing-resistant sign-in. Single sign-on through the company identity provider (the central service that signs employees in and vouches for them to every application) with hardware-key or passkey multi-factor authentication (a passkey is a login credential stored on the device and unlocked by fingerprint or PIN; FIDO2, which binds the login to the real site so a lookalike page cannot replay it). Device checks: only managed laptops, no personal devices.
- Least privilege by task, not by team. Roles map to what agents actually do: view order status, issue small refunds, reset a password. Agents see masked data by default (last four digits, partial email) and un-mask one field on demand with a reason, logged.
- Impersonation without exposure. If agents need to see what the customer sees, use a read-only "view as" session with a banner, a time limit and no access to payment or credential screens.
- Step-up and two-person rules on dangerous actions. Changing the account email or phone, resetting multi-factor authentication, exporting data, or refunds above a limit need re-authentication and approval by a second agent or lead. This directly counters social engineering, where an attacker persuades an agent to "just change the email".
- Rate limits and anomaly detection. Cap how many accounts one agent can open per hour. Alert when one agent looks up many unrelated accounts, or when actions happen outside shift or from a new location. The cap is a preventive control (it stops the action once the limit is hit); the alerts are detective controls (they find misuse after it starts), so together they back up the other preventive layers above.
- Customer-side verification. Agents cannot act until the customer passes verification the agent cannot override (code sent to the existing device), and the portal, not the agent, records that it happened. If the customer has lost that device, the fallback is a slower recovery path (a code to the registered email plus lead approval, or identity checks the agent cannot waive), never an agent judgment call.
Worked example. Preventive controls stop an action from happening, detective controls notice it, and corrective controls limit and undo the damage. An attacker phones support claiming to be a customer who lost their phone and asks to change the email. Preventive: the portal blocks the change until a code goes to the existing email, and requires lead approval because it is a high-risk action. Detective: the attempt is flagged because the agent's session had just looked up five unrelated accounts. Corrective: the lead can freeze the account and revoke the agent's session (end its login at once, so a stolen session token stops working) in one action.
Efficiency trade-off. Most tickets (status, small refunds) hit none of the step-ups. Friction lands on the rare sensitive actions, where a few extra minutes is acceptable. I would measure handle time before and after and tune thresholds.
Pitfalls. Shared logins, standing admin rights "for convenience", approvals that are rubber-stamped, and no way to revoke a session quickly. What would change my call: a very small team might use one approver pool; a regulated payment context would tighten refund limits and add recording.
What does secure by default mean, and how would you apply it to a service you ship to other teams? Describe the defaults you would choose for a cloud-hosted service and how you handle teams who need to loosen them.
Sample Answer
Direct answer
Secure by default means that when someone uses the service with zero configuration, the result is already the safe one. Insecure behavior must be something a team deliberately asks for, never something they must remember to turn off. For a cloud service shipped to other teams, the safe path should also be the easiest path.
Defaults I would choose for the service
| Area | Default |
|---|---|
| Network | No public endpoint, no inbound access except from named callers; outbound traffic limited to what the service needs |
| Authentication | Required on every endpoint; no default or shared passwords; single sign-on (SSO) for humans, per-service identities for machines |
| Access | Least privilege roles; the creator is not automatically an administrator |
| Data | Encryption at rest on (using a managed key, one held and rotated by the cloud provider's key service, with every use logged), TLS (encrypted transport) required with an old-version floor (refuse old protocol versions, for example anything below TLS 1.2, because they have known weaknesses), storage never public |
| Logging | Audit logging on and shipped to a separate account the service team cannot edit |
| Updates | Automatic patching of images and dependencies |
The same idea applied to a Linux host image and a fresh cloud VM
- OS: a minimal, hardened, centrally built image with only needed packages; patched on a schedule.
- Services: nothing listening that was not asked for; SSH with keys or a managed session tool, password login off, root login off.
- Network: the default firewall rule (security group) allows no inbound traffic; the instance metadata service requires session tokens (IMDSv2 on AWS) so a web bug cannot easily steal credentials. The instance metadata service is a local address that a cloud VM can query for facts about itself, including temporary cloud credentials. In its first version a plain web request to that address returns them, so a bug that makes the application fetch an attacker-chosen URL (server-side request forgery) can leak them. IMDSv2 requires first obtaining a session token with a special request, which that kind of bug usually cannot make.
- Storage: disks encrypted, object storage with public access blocked.
Handling teams who need to loosen a default
Make loosening possible but visible: a self-service exception request names the owner, the reason, the compensating control (an alternative safeguard, such as an IP allow list when a port must open), and an expiry date. Low-risk loosening is approved by the service owner. High-risk loosening, such as a public endpoint or turning off encryption, needs a security sign-off and may be accepted by the business owner who carries the risk. Exceptions expire automatically and are reviewed.
Worked example
A team needs a public endpoint for a partner webhook. Instead of opening the service, they get a time-limited exception that allows exactly one path through the gateway with a WAF rule (a filter limiting that path to expected methods, sizes and request shapes) and request signing (the partner attaches a signature computed with a shared secret over the request body, so the service can verify who sent it and that it was not altered), approved for 90 days and renewed only if still used.
Pitfalls
Defaults that break every team's first deploy get disabled wholesale, so test them with real users. Track the number of active exceptions as a signal: a rising count means a default is wrong, not that teams are careless.
Unlock Full Question Bank
Get access to all 37 Secure Architecture and Design Principles interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.