- Calculate the probability that all defence layers miss an attack and critically assess the independence assumption
- Explain zero-trust principles and the PDP/PEP components
- Model a context-based access policy in code
- Calculate the availability and yearly downtime of series and parallel systems
Classic networks were built like a “castle and moat”: a strong outer firewall and full trust for everything inside. Today staff work from home, data lives in the cloud, devices sit in pockets and contractors connect with their own laptops. One employee fooled by phishing is already “inside” and can move freely across the network. The zero-trust model is the answer: “never trust, always verify”.
Defence in depth: the probability of layers
Defence in depth builds several imperfect but different layers: training, physical security, the perimeter, segmentation, the host (EDR, updates), the application and the data (encryption, backups). An attacker has to get through all of them. If the layers are independent, the probabilities of independent events multiply:
- Pprobability that an attack gets through every layer
- pᵢprobability that layer i misses the attack (from 0 to 1)
- nnumber of layers
Derivation: if A and B are independent, P(A and B) = P(A) · P(B); by induction the rule extends to n layers.
The mail filter lets 10 % of phishing emails through, 20 % of employees who receive one click and type their password, and MFA fails to stop 1 % of such logins. Out of 10,000 phishing emails, how many end in an account takeover? And without MFA?
Show solutionHide solution
10,000 · 0.0002 = 2 accounts.
Without MFA: P = 0.1 · 0.2 = 0.02 → 10,000 · 0.02 = 200 accounts.
Conclusion: one layer cuts the risk 100 times because its pᵢ is 0.01.
Zero trust: principles and components
The US National Institute of Standards and Technology document NIST SP 800-207 (2020) describes zero-trust architecture. The key ideas: network location grants no automatic trust; every access to every resource is authenticated and authorised separately; rights are minimal and granted per session; decisions rely on several signals — identity, device health, behaviour, time and data sensitivity; everything is monitored continuously and designed on the assumption that a breach has already happened.
| Component | Role | Airport analogy |
|---|---|---|
| Policy engine (PE) | looks at the signals and decides “allow” or “deny” | the system that checks the passport, ticket and no-fly list |
| Policy administrator (PA) | opens or closes the session based on the decision | the desk that issues or cancels the boarding pass |
| Policy enforcement point (PEP) | the “gate” in front of the resource: holds the request and enforces the decision | the officer at the boarding gate |
SENSITIVITY = {'timetable': 1, 'grades': 2, 'payroll': 3}
def decide(req):
reasons = []
if not req['mfa']:
reasons.append('no MFA')
if not req['device_ok']:
reasons.append('device not compliant')
if SENSITIVITY[req['resource']] > req['clearance']:
reasons.append('role not allowed')
if SENSITIVITY[req['resource']] == 3 and req['risk'] != 'low':
reasons.append('sign-in risk too high')
return ('ALLOW', []) if not reasons else ('DENY', reasons)
requests = [
{'user': 'aysel', 'resource': 'grades', 'clearance': 2, 'mfa': True, 'device_ok': True, 'risk': 'low', 'network': 'home'},
{'user': 'murad', 'resource': 'grades', 'clearance': 2, 'mfa': True, 'device_ok': False, 'risk': 'low', 'network': 'office'},
{'user': 'leyla', 'resource': 'payroll', 'clearance': 3, 'mfa': True, 'device_ok': True, 'risk': 'high', 'network': 'office'},
{'user': 'elvin', 'resource': 'payroll', 'clearance': 1, 'mfa': False, 'device_ok': True, 'risk': 'low', 'network': 'office'},
]
for r in requests:
verdict, why = decide(r)
print(r['user'].ljust(6), r['network'].ljust(7), r['resource'].ljust(8), verdict, why)▸ Expected output
aysel home grades ALLOW [] murad office grades DENY ['device not compliant'] leyla office payroll DENY ['sign-in risk too high'] elvin office payroll DENY ['no MFA', 'role not allowed']
network field plays no part in the decision: Aysel is allowed from home, while Murad, Leyla and Elvin are denied even inside the office.Availability architecture: series and parallel
The “A” of the CIA triad is also a job for architecture. If components are connected in series (web server → database), the system works only when all of them work. If they are in parallel (two identical servers), the system stops only when all of them fail at once. For independent failures:
- Aseqavailability of the series system
- Aᵢprobability that component i is up
- Aparavailability of the parallel system
- 1 − Aᵢprobability that component i is down
Derivation: a parallel system is down only when every component is down; that event's probability is the product of the failure probabilities, and availability is its complement. Yearly downtime: D = (1 − A) · 8760 hours.
Two web servers, each 99 % available, run in parallel, and behind them a single database with 99.9 % availability is connected in series. Find the system's availability and how many hours a year it will be down.
Show solutionHide solution
System: 0.9999 · 0.999 = 0.9989 (≈ 99.89 %).
Downtime: (1 − 0.9989) · 8760 ≈ 9.6 hours/year.
The weak link is the database: it is a single point of failure (SPOF). Adding a second database replica makes the database tier 1 − 0.001² = 0.999999, and downtime falls to about 0.9 hours a year.
Connections: zero-trust ideas are applied in Google's BeyondCorp project, in cloud providers' identity services, in mutual TLS (mTLS) between microservices and in micro-segmentation. The availability formulas are used in cloud architecture (several data centres), in SRE practice and in service-level agreements (SLAs).
Write series and parallel (both taking any number of arguments), then evaluate a design with two databases: web tier = 99 % and 99 % in parallel, database tier = 99.9 % and 99.9 % in parallel, system = the two tiers in series. The print lines are already there.
def series(*parts):
# multiply all availabilities
return 1
def parallel(*parts):
# 1 - product of (1 - a)
return 1
web = parallel(0.99, 0.99)
db = parallel(0.999, 0.999)
total = series(web, db)
print(f'web tier: {web:.4%}')
print(f'db tier: {db:.6%}')
print(f'whole system: {total:.4%}')
print(f'downtime per year: {(1 - total) * 365 * 24:.2f} h')▸ Expected output
web tier: 99.9900% db tier: 99.999900% whole system: 99.9899% downtime per year: 0.88 h
Key points
- In defence in depth, the miss probabilities of independent layers multiply: P = p₁ · p₂ · … · pₙ; common-mode failures break this estimate.
- Zero trust: network location grants no trust; every request is checked by identity, device and context.
- NIST SP 800-207: the PE and PA decide (PDP), and the PEP enforces the decision in front of the resource.
- Series: A = ∏Aᵢ; parallel: A = 1 − ∏(1 − Aᵢ); yearly downtime D = (1 − A) · 8760 h.
- Each extra “nine” cuts downtime tenfold; remove single points of failure with redundancy.
Check yourself
10 questions. Every correct answer earns XP.