Strategie voor gelaagde verdediging
Defense in depth implementeren voor AI-applicaties: onafhankelijke verdedigingslagen ontwerpen, orthogonale dekking borgen en de complexiteit van meerlaagse beveiliging beheersen.
Layered Defense Strategy
Gelaagde verdediging (defense in depth) is de meest effectieve strategie om de fundamentele asymmetrie tussen aanval en verdediging in AI-systemen aan te pakken. Geen enkel verdedigingsmechanisme kan alle aanvalstypes afdekken, en elke individuele verdediging kent bekende bypass-technieken. Door meerdere onafhankelijke verdedigingen te stapelen, daalt de kans dat een aanvaller alle lagen tegelijk omzeilt dramatisch -- mits de lagen echt onafhankelijk zijn.
Architectuur van verdedigingslagen
Referentiearchitectuur
Een productie-AI-systeem met gelaagde verdediging:
Request Flow:
User Input
│
▼
┌─────────────────────────────────────────────┐
│ Layer 1: Perimeter Defense │
│ ├── Rate limiting │
│ ├── Authentication & authorization │
│ └── Input size and format validation │
└──────────────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ Layer 2: Input Analysis │
│ ├── Prompt shield (ML classifier) │
│ ├── Unicode/encoding normalization │
│ └── Known pattern blocklist │
└──────────────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ Layer 3: Context Security │
│ ├── Instruction hierarchy enforcement │
│ ├── Retrieved content sanitization │
│ └── Data source isolation │
└──────────────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ Layer 4: Model + Inference │
│ ├── Safety-aligned model (RLHF/DPO) │
│ ├── System prompt with safety instructions │
│ └── Temperature and sampling controls │
└──────────────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ Layer 5: Output Validation │
│ ├── Content safety classifier │
│ ├── PII detection and redaction │
│ ├── Tool call validation │
│ └── Response format enforcement │
└──────────────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ Layer 6: Post-Delivery Monitoring │
│ ├── Audit logging │
│ ├── Anomaly detection │
│ └── Incident alerting │
└──────────────────────┬──────────────────────┘
│
▼
Response
Onafhankelijkheid van lagen
De cruciale eigenschap die gelaagde verdediging effectief maakt, is onafhankelijkheid: het falen van één laag mag de kans dat een andere laag faalt niet vergroten.
| Onafhankelijke lagen | Gecorreleerde lagen (slecht) |
|---|---|
| Keyword-filter + semantische classifier | Twee verschillende keyword-filters |
| Inputanalyse + outputvalidatie | Hetzelfde ML-model voor input en output |
| Rate limiting + content-classifier | Twee classifiers getraind op dezelfde data |
| Architectonische beperkingen + ML-verdediging | ML-verdediging + ML-monitor (zelfde model) |
def verify_layer_independence(defense_layers, attack_set):
"""
Test of verdedigingslagen onafhankelijk falen.
Gecorreleerde fouten wijzen op gedeelde zwakke plekken.
"""
layer_results = {}
for attack in attack_set:
for layer_name, layer in defense_layers.items():
blocked = layer.check(attack)["blocked"]
if layer_name not in layer_results:
layer_results[layer_name] = []
layer_results[layer_name].append(blocked)
# Controleer de paarsgewijze correlatie
correlations = {}
layer_names = list(layer_results.keys())
for i, name_a in enumerate(layer_names):
for j, name_b in enumerate(layer_names):
if i < j:
results_a = layer_results[name_a]
results_b = layer_results[name_b]
# Bereken de correlatie
both_fail = sum(
1 for a, b in zip(results_a, results_b)
if not a and not b
)
either_fail = sum(
1 for a, b in zip(results_a, results_b)
if not a or not b
)
correlation = both_fail / max(either_fail, 1)
correlations[f"{name_a} <-> {name_b}"] = {
"correlation": correlation,
"independent": correlation < 0.3
}
return correlationsImplementatiepatronen
Patroon 1: Fail-open vs fail-closed
Elke laag moet besluiten wat te doen wanneer hij een fout tegenkomt:
class FailClosedLayer:
"""Blokkeer het verzoek als de verdedigingslaag een fout tegenkomt."""
def check(self, input_text):
try:
result = self.classifier.classify(input_text)
return result
except Exception as e:
# Fout in verdediging -> blokkeer het verzoek (conservatief)
logger.error(f"Defense layer error: {e}")
return {"blocked": True, "reason": "defense_error"}
class FailOpenLayer:
"""Laat het verzoek door als de verdedigingslaag een fout tegenkomt."""
def check(self, input_text):
try:
result = self.classifier.classify(input_text)
return result
except Exception as e:
# Fout in verdediging -> laat door (permissief)
logger.warning(f"Defense layer error, failing open: {e}")
return {"blocked": False, "reason": "defense_error_failopen"}Patroon 2: Async vs sync lagen
Niet alle lagen hoeven synchroon te zijn. Sommige kunnen asynchroon werken zonder het antwoord te blokkeren:
class LayeredDefenseOrchestrator:
"""Orkestreer synchrone en asynchrone verdedigingslagen."""
def __init__(self):
self.sync_layers = [] # Moeten slagen vóór het antwoord
self.async_layers = [] # Draaien op de achtergrond
def process(self, request):
# Synchrone lagen: blokkeer als er een faalt
for layer in self.sync_layers:
result = layer.check(request)
if result["blocked"]:
return self.block_response(result)
# Verwerk het verzoek (genereer een antwoord)
response = self.generate_response(request)
# Synchrone outputlagen
for layer in self.sync_output_layers:
result = layer.check(response)
if result["blocked"]:
return self.block_response(result)
# Async lagen: log en monitor zonder te blokkeren
for layer in self.async_layers:
self.background_queue.put((layer, request, response))
return responsePatroon 3: Escalatieketens
Sommige aanvallen rechtvaardigen het escaleren van geautomatiseerde verdediging naar menselijke beoordeling:
class EscalationChain:
"""Escaleer verdachte verzoeken via toenemende mate van toetsing."""
def __init__(self):
self.levels = [
{"name": "automated_fast", "threshold": 0.3, "action": "allow"},
{"name": "automated_deep", "threshold": 0.6, "action": "enhanced_check"},
{"name": "human_review_queue", "threshold": 0.8, "action": "queue"},
{"name": "block", "threshold": 0.95, "action": "block"},
]
def evaluate(self, request, risk_score):
"""Route het verzoek op basis van de risicoscore."""
for level in self.levels:
if risk_score < level["threshold"]:
return {
"action": level["action"],
"level": level["name"],
"risk_score": risk_score
}
return {"action": "block", "level": "maximum", "risk_score": risk_score}Performancebeheer
Gelaagde verdediging voegt latentie toe. Dit beheersen is cruciaal voor de gebruikerservaring:
Toewijzing van het latentiebudget
# Totaal latentiebudget: 2000ms (typisch voor chatapplicaties)
LATENCY_BUDGET = {
"perimeter": 5, # Rate limit-check: 5ms
"input_analysis": 50, # ML-classifier: 50ms
"context_security": 20, # Template-controle: 20ms
"model_inference": 1500, # LLM-generatie: 1500ms
"output_validation": 100, # Safety-classifier: 100ms
"post_delivery": 0, # Async, geen extra latentie
"overhead": 25, # Netwerk, serialisatie: 25ms
"buffer": 300, # Marge voor variantie: 300ms
}
# Totaal: 2000msParallelle uitvoering van lagen
import asyncio
async def parallel_input_defense(request):
"""Draai onafhankelijke input-verdedigingslagen parallel."""
results = await asyncio.gather(
prompt_shield.check_async(request),
encoding_normalizer.check_async(request),
pattern_blocklist.check_async(request),
return_exceptions=True
)
# Blokkeer als een laag het verzoek signaleert
for result in results:
if isinstance(result, Exception):
continue # Individuele laag faalt, ga door
if result.get("blocked"):
return result
return {"blocked": False}Gelaagde verdediging testen
Laag-voor-laag testen
Test elke laag afzonderlijk om de individuele bijdrage ervan te begrijpen:
def layer_contribution_analysis(layers, attack_set):
"""Meet de marginale bijdrage van elke laag aan de verdediging."""
all_layers_result = evaluate_defense(layers, attack_set)
contributions = {}
for layer_name in layers:
# Verwijder deze laag en evalueer
remaining = {k: v for k, v in layers.items() if k != layer_name}
without_layer = evaluate_defense(remaining, attack_set)
contributions[layer_name] = {
"block_rate_with": all_layers_result["block_rate"],
"block_rate_without": without_layer["block_rate"],
"marginal_contribution": (
all_layers_result["block_rate"] -
without_layer["block_rate"]
),
"is_redundant": (
all_layers_result["block_rate"] ==
without_layer["block_rate"]
)
}
return contributionsGerelateerde onderwerpen
- Defense Taxonomy — Catalogus van beschikbare verdedigingen
- Defense Evaluation — De effectiviteit van verdedigingen meten
- Defense Economics — Kosten-batenanalyse van elke laag
Een systeem heeft drie verdedigingslagen. Uit tests blijkt dat wanneer Layer 1 (keyword-filter) wordt omzeild, Layer 2 (een ML-classifier getraind op de trainingsdata van het keyword-filter) ook in 80% van de gevallen wordt omzeild. Wat is het probleem?
Referenties
- NIST, "Framework for Improving Critical Infrastructure Cybersecurity" (2018)
- OWASP, "Defense in Depth for LLM Applications" (2024)
- Microsoft, "Azure AI Content Safety: Multi-layered defense" (2024)