Security by Design
debt(d9/e9/b9/t7)
Closest to 'silent in production until users hit it' (d9). detection_hints.automated is 'no' — absence of threat modelling or security-by-design thinking cannot be detected by any tool; it manifests only when vulnerabilities surface in production or audit.
Closest to 'architectural rework' (e9). quick_fix suggests threat modelling before writing code, but retrofitting security-by-design into an existing system means restructuring trust boundaries and data flows — common_mistakes explicitly notes 'architectural flaws cannot be patched'.
Closest to 'defines the system's shape' (b9). applies_to covers web/api/cli — this is an SDLC-level principle (per tags: principles, architecture, sdlc) that shapes every architectural decision; absence or presence defines the entire system's security posture.
Closest to 'serious trap' (t7). The misconception is explicit: developers believe security by design means 'adding security features' (checks, libraries) when it actually means making insecure states structurally impossible — this contradicts the intuitive reading of the name.
Also Known As
TL;DR
Explanation
Security by Design (SbD) means security is a first-class requirement alongside functionality — considered during threat modelling, system architecture, data flow design, and API contracts, not discovered during a pre-release penetration test. Core principles include: minimise attack surface, apply least privilege at every layer, use defence in depth, fail securely (default-deny), avoid security through obscurity, keep design simple (complexity breeds vulnerabilities), and separate duties. For PHP projects this means: choosing framework defaults that are secure out of the box, designing data flows that never touch user input without sanitisation, and reviewing every new feature through a threat-model lens.
Common Misconception
Why It Matters
Common Mistakes
- Security as a final phase ('we'll add security later') — architectural flaws cannot be patched.
- Designing for the happy path only — not considering what happens when inputs are malicious.
- No threat modelling in the design phase — security gaps are discovered in production.
- Prioritising features over security constraints in early sprints — security debt compounds quickly.
Code Examples
// Design that makes security impossible to add later:
class UserController {
// All user data in one object — cannot restrict field visibility per role
// No audit log hook points — cannot add logging without rewriting
// Direct DB calls everywhere — cannot add encryption layer without touching all code
// Feature shipped — security 'added later' means full rewrite
}
// Security by design — bake it in from the start
// 1. Default deny
class Policy {
public function can(User $user, string $action, mixed $resource): bool {
return false; // deny by default — explicitly grant per action
}
}
// 2. Fail securely — errors don't open access
function getUser(int $id): User {
return User::findOrFail($id); // throws, not returns null
}
// 3. Immutable value objects — can't enter invalid state
readonly class Email {
public function __construct(public string $value) {
if (!filter_var($value, FILTER_VALIDATE_EMAIL))
throw new \InvalidArgumentException("Invalid email: $value");
}
}
// 4. Least privilege — each component gets minimum permissions it needs