DEV Community

Cover image for The Swiss Cheese Model of AI Security — Why Single-Layer Defense Always Fails
Ken Imoto
Ken Imoto

Posted on

The Swiss Cheese Model of AI Security — Why Single-Layer Defense Always Fails

I was on a flight today, and a thought hit me: radio signals can interfere with avionics — so why don't airlines just confiscate everyone's phones? Why not install a signal jammer on board?

The answer: they don't need to, because the plane is already safe without it.

Aviation safety doesn't rely on a single countermeasure:

  • "Please switch to airplane mode" announcements (behavioral control)
  • Electromagnetic shielding on the airframe (technical defense)
  • Frequency band separation (defense by design)
  • Pilot backup instruments (redundancy)

If one layer is breached, the next one holds. This is the idea behind the Swiss Cheese Model — a concept from aviation safety researcher James Reason. Each defense layer is like a slice of Swiss cheese: full of holes. But stack enough slices together, and the holes don't align.

And this maps directly onto AI security.

Every Defense Layer Has Holes

I've spent months testing AI security tooling in real projects. Here's what I've found:

CLAUDE.md / System Prompts
→ Holes: Prompt injection can override instructions. Adversarial inputs bypass guardrails. A well-crafted payload can make the model ignore its own safety rules.

OWASP ZAP (Dynamic Application Security Testing)
→ Holes: Catches injection attacks and misconfigurations, but blind to business logic flaws, race conditions, and TOCTOU vulnerabilities.

Claude Code Security Review (LLM-powered SAST)
→ Holes: Excellent at pattern recognition across large codebases (OpenAI's o3 found a Linux kernel race condition CVE in 12,000+ lines). But LLMs can hallucinate false positives and miss novel attack vectors they haven't been trained on.

Human Code Review
→ Holes: Humans get fatigued. They miss subtle timing issues. They skim 500-line PRs. And they have blind spots shaped by their own experience.

No single layer is reliable enough on its own. That's the point — just like no single measure on that airplane is enough by itself.

The Real Danger: When Holes Align

Consider this scenario from a real codebase:

def check_auth(request):
    try:
        session = session_store.get(request.session_id)
        return session.is_authenticated
    except TimeoutError:
        return True  # "Just let them through for now"
Enter fullscreen mode Exit fullscreen mode

This authentication bypass only triggers when the session store is under memory pressure. It won't appear in dev. It won't appear in staging. OWASP ZAP won't catch it because it's a business logic flaw, not a traditional vulnerability.

Three holes aligned:

  • Resource exhaustion (memory pressure)
  • Poor exception handling (fail-open instead of fail-closed)
  • Insufficient testing (no load tests covering auth paths)

Each one seems minor in isolation. Together, they create an authentication bypass in production.

Defense in Depth: A Practical 4-Layer Stack

Here's the multi-layer defense I use in practice:

Layer 1 — AI-Powered Static Analysis

Claude Code Security Review in CI/CD. It catches race conditions, TOCTOU issues, and business logic flaws that traditional SAST tools miss.

Layer 2 — Dynamic Testing (DAST + Chaos)

OWASP ZAP for vulnerability scanning. Toxiproxy for injecting network failures. Go's race detector with -race flag. Test what happens when things break, not just when they work.

Layer 3 — Circuit Breakers & Fail-Safe Patterns

Never fail open. When an external service times out, the circuit breaker trips — blocking cascading failures instead of letting retry storms bring down the entire system.

CLOSED (normal) → failures exceed threshold → OPEN (blocking)
OPEN → recovery timeout expires → HALF-OPEN (testing)
HALF-OPEN → success → CLOSED / failure → OPEN
Enter fullscreen mode Exit fullscreen mode

Layer 4 — Human Review with Context

Not just "LGTM." Reviewers armed with the output from Layers 1-3 can focus on what machines can't catch: architectural decisions, threat modeling, and "does this make business sense?"

MCP Tool Poisoning: A Case Study

I recently tested MCP (Model Context Protocol) tool poisoning — where a malicious tool description manipulates an AI agent into executing unintended actions.

The defense that worked? Not any single layer. It was the combination:

  • CLAUDE.md rules flagging suspicious tool behavior
  • Human review catching the manipulated tool descriptions
  • Circuit breaker patterns preventing cascading execution

Remove any one layer, and the attack succeeds.

Where to Start

You don't need all four layers everywhere. Prioritize by risk × impact:

  • Payment & authentication → All 4 layers. Non-negotiable.
  • Database write operations → Race detection + LLM review.
  • External API integrations → Chaos testing + circuit breakers.
  • Batch processing → Load testing is usually sufficient.

Back on that airplane, nobody panics about a single passenger forgetting to turn off their phone. The system is designed to handle it. That's what good security looks like.

You don't need perfect defenses. You need enough imperfect ones that the holes never align.

Start with one additional layer this week. Your future self will thank you.


About the Author

Ken Imoto — Software engineer focused on AI-assisted development, security, and DevOps.

Top comments (0)