Security¶
Security is the system's ability to protect data and functionality from unauthorised access, modification, or disclosure. It is a cross-cutting characteristic: it constrains every layer, and unlike most characteristics it has an active adversary rather than a statistical failure mode.
The properties being protected¶
- Confidentiality: only authorised parties can read the data.
- Integrity: data cannot be altered undetected.
- Availability: the system stays usable (see Availability; denial of service is a security concern too).
- Authenticity: the claimed identity is real.
- Non-repudiation / auditability: actions can be attributed after the fact.
Design principles¶
- Defence in depth. No single control is trusted alone; a bypassed WAF should still meet authorisation checks in the service.
- Least privilege. Every service, credential, and person gets the narrowest scope that works, with short lifetimes.
- Secure by default. Deny unless explicitly allowed. New endpoints must require authentication because the framework says so, not because someone remembered.
- Validate at trust boundaries. Everything crossing into the system, request bodies, headers, files, messages from other services, is untrusted input, validated against an allow-list.
- Minimise the attack surface and the data. Data you never store cannot leak; endpoints you never expose cannot be attacked.
- Fail securely. An error in an authorisation check must deny, never fall through to allow.
- No security by obscurity. Assume the design is public; only keys are secret.
flowchart LR
U[User] -->|TLS| E[Edge: WAF, rate limit]
E --> G[Gateway: authenticate, verify token]
G --> S[Service: authorise per resource]
S --> D[(Encrypted at rest,<br/>least-privilege credentials)]
S --> L[[Audit log]]
Concrete practices¶
- Authentication vs authorisation. Authentication proves who; authorisation decides what. Authorisation must be checked per resource in the service, not only at the gateway, a valid token is not permission to read someone else's order.
- Encrypt in transit and at rest. TLS everywhere, including inside the network.
- Hash passwords with a slow, salted algorithm (bcrypt, scrypt, Argon2). Never encrypt them, never invent the scheme.
- Parameterised queries and safe encoding to close injection classes structurally rather than by review.
- Manage secrets in a dedicated store with rotation; never in the repository or the image. See Configurability.
- Patch dependencies. Most breaches use known vulnerabilities in old libraries, not novel attacks.
- Log security events: authentication, authorisation failures, privilege changes, without logging the secrets themselves.
Trade-offs¶
- Against usability: every additional check is friction; make the secure path the easy path rather than adding steps.
- Against performance: encryption, token validation, and auditing all cost latency.
- Against deployability: strict change control conflicts with frequent releases; automate the controls into the pipeline instead of gating with humans.
Fitness functions¶
- Dependency and container scanning failing the build on known critical CVEs.
- Static analysis and secret scanning on every commit.
- An automated test asserting every route requires authentication unless explicitly annotated public.
- Regular penetration tests and threat-modelling reviews on architectural changes.
Check Your Understanding¶
#
A valid, authenticated token arrives at a service requesting order #5001, which belongs to another customer. What must happen?
#
What does "fail securely" mean for an authorisation check that throws an exception?