1376 Zero Trust vs Perimeter Security
Animation comparing castle-and-moat vs never-trust-always-verify models
1376.1 Security Model Comparison
This animation compares traditional perimeter-based security (“castle and moat”) with Zero Trust architecture. Watch how an attacker’s behavior differs when attempting to breach each model and move laterally within the network.
- Play Attack Simulation: Click “Play Attack” to watch the full animated attack sequence unfold across both security models simultaneously
- Step Through Phases: Use “Step Forward” to advance one phase at a time and examine each stage in detail
- Compare Results: Observe how the left panel (Perimeter) shows full network compromise while the right panel (Zero Trust) contains the attack
- Reset and Replay: Click “Reset” to start over and watch the attack again with fresh observations
Tips: - Watch the status indicators at the bottom of each panel to track detection and response - Notice how MFA checkpoints in Zero Trust block lateral movement attempts - Compare the final state summary boxes to understand the security difference
Show code
viewof zeroTrustAnimation = {
const width = 900;
const height = 550;
// IEEE Colors
const colors = {
navy: "#2C3E50",
teal: "#16A085",
orange: "#E67E22",
gray: "#7F8C8D",
red: "#E74C3C",
green: "#27AE60",
lightGray: "#ECF0F1",
white: "#FFFFFF"
};
// Create container
const container = d3.create("div")
.style("font-family", "system-ui, -apple-system, sans-serif")
.style("max-width", "900px")
.style("margin", "0 auto");
// Title
container.append("div")
.style("text-align", "center")
.style("margin-bottom", "15px")
.append("h3")
.style("color", colors.navy)
.style("margin", "0")
.text("Zero Trust vs Perimeter Security");
// Control panel
const controls = container.append("div")
.style("display", "flex")
.style("justify-content", "center")
.style("gap", "10px")
.style("margin-bottom", "15px")
.style("flex-wrap", "wrap");
const buttonStyle = (btn) => {
btn.style("padding", "10px 20px")
.style("font-size", "14px")
.style("font-weight", "600")
.style("border", "none")
.style("border-radius", "6px")
.style("cursor", "pointer")
.style("transition", "all 0.2s ease");
};
const playBtn = controls.append("button")
.text("Play Attack")
.style("background", colors.teal)
.style("color", colors.white)
.call(buttonStyle);
const stepBtn = controls.append("button")
.text("Step Forward")
.style("background", colors.orange)
.style("color", colors.white)
.call(buttonStyle);
const resetBtn = controls.append("button")
.text("Reset")
.style("background", colors.gray)
.style("color", colors.white)
.call(buttonStyle);
// Phase indicator
const phaseDisplay = container.append("div")
.style("text-align", "center")
.style("margin-bottom", "10px")
.style("font-size", "14px")
.style("color", colors.navy);
// SVG canvas
const svg = container.append("svg")
.attr("viewBox", `0 0 ${width} ${height}`)
.attr("width", "100%")
.style("background", colors.lightGray)
.style("border-radius", "8px")
.style("display", "block");
// Definitions for gradients and markers
const defs = svg.append("defs");
// Firewall gradient
const firewallGrad = defs.append("linearGradient")
.attr("id", "firewallGrad")
.attr("x1", "0%").attr("y1", "0%")
.attr("x2", "0%").attr("y2", "100%");
firewallGrad.append("stop").attr("offset", "0%").attr("stop-color", colors.orange);
firewallGrad.append("stop").attr("offset", "100%").attr("stop-color", "#D35400");
// Attack arrow marker
defs.append("marker")
.attr("id", "attackArrow")
.attr("viewBox", "0 0 10 10")
.attr("refX", 9).attr("refY", 5)
.attr("markerWidth", 6).attr("markerHeight", 6)
.attr("orient", "auto")
.append("path")
.attr("d", "M 0 0 L 10 5 L 0 10 z")
.attr("fill", colors.red);
// Create two side panels
const leftPanel = svg.append("g").attr("transform", "translate(0, 0)");
const rightPanel = svg.append("g").attr("transform", "translate(450, 0)");
// Panel backgrounds
leftPanel.append("rect")
.attr("x", 5).attr("y", 40)
.attr("width", 440).attr("height", 500)
.attr("fill", colors.white)
.attr("stroke", colors.gray)
.attr("stroke-width", 1)
.attr("rx", 8);
rightPanel.append("rect")
.attr("x", 5).attr("y", 40)
.attr("width", 440).attr("height", 500)
.attr("fill", colors.white)
.attr("stroke", colors.gray)
.attr("stroke-width", 1)
.attr("rx", 8);
// Panel titles
leftPanel.append("text")
.attr("x", 225).attr("y", 65)
.attr("text-anchor", "middle")
.attr("font-size", "16px")
.attr("font-weight", "bold")
.attr("fill", colors.navy)
.text("Perimeter Security (Castle & Moat)");
rightPanel.append("text")
.attr("x", 225).attr("y", 65)
.attr("text-anchor", "middle")
.attr("font-size", "16px")
.attr("font-weight", "bold")
.attr("fill", colors.teal)
.text("Zero Trust Architecture");
// === LEFT PANEL: Perimeter Security ===
// Outer moat (water)
leftPanel.append("ellipse")
.attr("cx", 225).attr("cy", 310)
.attr("rx", 180).attr("ry", 140)
.attr("fill", "#3498DB")
.attr("opacity", 0.3);
// Castle wall (firewall)
leftPanel.append("ellipse")
.attr("cx", 225).attr("cy", 310)
.attr("rx", 140).attr("ry", 110)
.attr("fill", "none")
.attr("stroke", "url(#firewallGrad)")
.attr("stroke-width", 8)
.attr("stroke-dasharray", "15,5");
// Firewall label
leftPanel.append("text")
.attr("x", 225).attr("y", 195)
.attr("text-anchor", "middle")
.attr("font-size", "11px")
.attr("fill", colors.orange)
.attr("font-weight", "bold")
.text("FIREWALL");
// Internal network nodes (trusted zone)
const leftNodes = [
{ x: 160, y: 270, label: "Server A", type: "server" },
{ x: 290, y: 270, label: "Server B", type: "server" },
{ x: 160, y: 350, label: "Database", type: "db" },
{ x: 290, y: 350, label: "User PC", type: "pc" },
{ x: 225, y: 310, label: "Core", type: "core" }
];
// Internal connections (fully meshed - trusted)
const leftConnections = leftPanel.append("g").attr("class", "left-connections");
leftNodes.forEach((n1, i) => {
leftNodes.forEach((n2, j) => {
if (i < j) {
leftConnections.append("line")
.attr("x1", n1.x).attr("y1", n1.y)
.attr("x2", n2.x).attr("y2", n2.y)
.attr("stroke", colors.gray)
.attr("stroke-width", 1)
.attr("stroke-dasharray", "3,3")
.attr("opacity", 0.5);
}
});
});
// Draw nodes
const leftNodeGroup = leftPanel.append("g").attr("class", "left-nodes");
leftNodes.forEach(node => {
const g = leftNodeGroup.append("g")
.attr("transform", `translate(${node.x}, ${node.y})`)
.attr("class", `node-${node.label.replace(/\s/g, '')}`);
g.append("circle")
.attr("r", 22)
.attr("fill", colors.teal)
.attr("stroke", colors.navy)
.attr("stroke-width", 2);
g.append("text")
.attr("y", 35)
.attr("text-anchor", "middle")
.attr("font-size", "10px")
.attr("fill", colors.navy)
.text(node.label);
});
// Trusted zone label
leftPanel.append("text")
.attr("x", 225).attr("y", 420)
.attr("text-anchor", "middle")
.attr("font-size", "10px")
.attr("fill", colors.gray)
.text("\"Trusted\" Internal Zone");
// Attacker starting position (left)
const leftAttacker = leftPanel.append("g")
.attr("class", "left-attacker")
.attr("transform", "translate(40, 310)")
.style("opacity", 0);
leftAttacker.append("circle")
.attr("r", 15)
.attr("fill", colors.red);
leftAttacker.append("text")
.attr("text-anchor", "middle")
.attr("y", 5)
.attr("font-size", "14px")
.attr("fill", colors.white)
.text("!");
// Attack path visualization (left)
const leftAttackPath = leftPanel.append("g").attr("class", "left-attack-path");
// === RIGHT PANEL: Zero Trust ===
// Micro-segments
const segments = [
{ x: 120, y: 150, w: 100, h: 80, label: "Segment A" },
{ x: 240, y: 150, w: 100, h: 80, label: "Segment B" },
{ x: 120, y: 280, w: 100, h: 80, label: "Segment C" },
{ x: 240, y: 280, w: 100, h: 80, label: "Segment D" },
{ x: 180, y: 400, w: 100, h: 70, label: "Core" }
];
segments.forEach(seg => {
const g = rightPanel.append("g")
.attr("class", `segment-${seg.label.replace(/\s/g, '')}`);
g.append("rect")
.attr("x", seg.x).attr("y", seg.y)
.attr("width", seg.w).attr("height", seg.h)
.attr("fill", colors.lightGray)
.attr("stroke", colors.teal)
.attr("stroke-width", 2)
.attr("rx", 4);
g.append("text")
.attr("x", seg.x + seg.w/2)
.attr("y", seg.y + 15)
.attr("text-anchor", "middle")
.attr("font-size", "10px")
.attr("fill", colors.navy)
.attr("font-weight", "bold")
.text(seg.label);
});
// Zero Trust nodes with verification icons
const rightNodes = [
{ x: 170, y: 200, label: "Server A", seg: 0 },
{ x: 290, y: 200, label: "Server B", seg: 1 },
{ x: 170, y: 320, label: "Database", seg: 2 },
{ x: 290, y: 320, label: "User PC", seg: 3 },
{ x: 230, y: 440, label: "Identity", seg: 4 }
];
// Verification checkpoints between segments
const checkpoints = [
{ x: 220, y: 175, connects: [0, 1] },
{ x: 220, y: 300, connects: [2, 3] },
{ x: 145, y: 240, connects: [0, 2] },
{ x: 315, y: 240, connects: [1, 3] },
{ x: 170, y: 380, connects: [2, 4] },
{ x: 260, y: 380, connects: [3, 4] }
];
const rightCheckpointGroup = rightPanel.append("g").attr("class", "right-checkpoints");
checkpoints.forEach((cp, i) => {
const g = rightCheckpointGroup.append("g")
.attr("transform", `translate(${cp.x}, ${cp.y})`)
.attr("class", `checkpoint-${i}`);
g.append("rect")
.attr("x", -12).attr("y", -8)
.attr("width", 24).attr("height", 16)
.attr("fill", colors.orange)
.attr("rx", 3);
g.append("text")
.attr("text-anchor", "middle")
.attr("y", 4)
.attr("font-size", "8px")
.attr("fill", colors.white)
.attr("font-weight", "bold")
.text("MFA");
});
// Draw Zero Trust nodes
const rightNodeGroup = rightPanel.append("g").attr("class", "right-nodes");
rightNodes.forEach(node => {
const g = rightNodeGroup.append("g")
.attr("transform", `translate(${node.x}, ${node.y})`)
.attr("class", `zt-node-${node.label.replace(/\s/g, '')}`);
g.append("circle")
.attr("r", 20)
.attr("fill", colors.teal)
.attr("stroke", colors.navy)
.attr("stroke-width", 2);
// Shield icon for verification
g.append("path")
.attr("d", "M0,-8 L6,-4 L6,4 C6,8 0,10 0,10 C0,10 -6,8 -6,4 L-6,-4 Z")
.attr("fill", colors.white)
.attr("opacity", 0.8);
g.append("text")
.attr("y", 32)
.attr("text-anchor", "middle")
.attr("font-size", "9px")
.attr("fill", colors.navy)
.text(node.label);
});
// Zero Trust principle label
rightPanel.append("text")
.attr("x", 225).attr("y", 500)
.attr("text-anchor", "middle")
.attr("font-size", "10px")
.attr("fill", colors.teal)
.attr("font-weight", "bold")
.text("\"Never Trust, Always Verify\"");
// Attacker starting position (right)
const rightAttacker = rightPanel.append("g")
.attr("class", "right-attacker")
.attr("transform", "translate(40, 200)")
.style("opacity", 0);
rightAttacker.append("circle")
.attr("r", 15)
.attr("fill", colors.red);
rightAttacker.append("text")
.attr("text-anchor", "middle")
.attr("y", 5)
.attr("font-size", "14px")
.attr("fill", colors.white)
.text("!");
// Attack path visualization (right)
const rightAttackPath = rightPanel.append("g").attr("class", "right-attack-path");
// Status indicators
const leftStatus = leftPanel.append("g")
.attr("transform", "translate(225, 480)");
leftStatus.append("rect")
.attr("x", -80).attr("y", -12)
.attr("width", 160).attr("height", 24)
.attr("fill", colors.lightGray)
.attr("stroke", colors.gray)
.attr("rx", 4);
const leftStatusText = leftStatus.append("text")
.attr("text-anchor", "middle")
.attr("y", 5)
.attr("font-size", "11px")
.attr("fill", colors.gray)
.text("Status: Secure");
const rightStatus = rightPanel.append("g")
.attr("transform", "translate(225, 520)");
rightStatus.append("rect")
.attr("x", -80).attr("y", -12)
.attr("width", 160).attr("height", 24)
.attr("fill", colors.lightGray)
.attr("stroke", colors.gray)
.attr("rx", 4);
const rightStatusText = rightStatus.append("text")
.attr("text-anchor", "middle")
.attr("y", 5)
.attr("font-size", "11px")
.attr("fill", colors.gray)
.text("Status: Secure");
// Animation state
let currentPhase = 0;
let isPlaying = false;
let animationTimer = null;
const phases = [
{ name: "Initial State", desc: "Both networks secure, attacker outside" },
{ name: "Perimeter Probe", desc: "Attacker probes firewall for vulnerabilities" },
{ name: "Firewall Breach", desc: "Attacker finds exploit, breaches perimeter" },
{ name: "Inside Perimeter", desc: "Perimeter: Full access! Zero Trust: Blocked at checkpoint" },
{ name: "Lateral Movement", desc: "Perimeter: Free movement. Zero Trust: Each hop requires verification" },
{ name: "Data Exfiltration", desc: "Perimeter: Data stolen! Zero Trust: Attack contained" },
{ name: "Final State", desc: "Compare the damage between models" }
];
function updatePhaseDisplay() {
const phase = phases[currentPhase];
phaseDisplay.html(`<strong>Phase ${currentPhase + 1}/${phases.length}:</strong> ${phase.name}<br><span style="color: ${colors.gray}; font-size: 12px;">${phase.desc}</span>`);
}
function drawAttackLine(panel, x1, y1, x2, y2, success = true) {
const path = panel.select(".left-attack-path, .right-attack-path")
.append("line")
.attr("x1", x1).attr("y1", y1)
.attr("x2", x1).attr("y2", y1)
.attr("stroke", success ? colors.red : colors.gray)
.attr("stroke-width", 2)
.attr("stroke-dasharray", success ? "none" : "4,4")
.attr("marker-end", success ? "url(#attackArrow)" : "none");
return path.transition().duration(500)
.attr("x2", x2).attr("y2", y2);
}
function showBlockedIndicator(panel, x, y) {
const blocked = panel.append("g")
.attr("transform", `translate(${x}, ${y})`)
.attr("class", "blocked-indicator")
.style("opacity", 0);
blocked.append("circle")
.attr("r", 18)
.attr("fill", "none")
.attr("stroke", colors.red)
.attr("stroke-width", 3);
blocked.append("line")
.attr("x1", -10).attr("y1", -10)
.attr("x2", 10).attr("y2", 10)
.attr("stroke", colors.red)
.attr("stroke-width", 3);
blocked.append("text")
.attr("y", 30)
.attr("text-anchor", "middle")
.attr("font-size", "9px")
.attr("fill", colors.red)
.attr("font-weight", "bold")
.text("BLOCKED");
blocked.transition().duration(300).style("opacity", 1);
return blocked;
}
function animatePhase(phase) {
currentPhase = phase;
updatePhaseDisplay();
switch(phase) {
case 0: // Initial state
resetAnimation();
leftAttacker.transition().duration(500).style("opacity", 1);
rightAttacker.transition().duration(500).style("opacity", 1);
break;
case 1: // Perimeter probe
// Show probing attempts on firewall
for (let i = 0; i < 3; i++) {
setTimeout(() => {
const angle = (i * 40 - 20) * Math.PI / 180;
const x = 85 + Math.cos(angle) * 30;
const y = 310 + Math.sin(angle) * 30;
leftAttackPath.append("circle")
.attr("cx", 40).attr("cy", 310)
.attr("r", 3)
.attr("fill", colors.red)
.transition().duration(400)
.attr("cx", x).attr("cy", y)
.style("opacity", 0)
.remove();
}, i * 200);
}
// Same for right
for (let i = 0; i < 3; i++) {
setTimeout(() => {
rightAttackPath.append("circle")
.attr("cx", 40).attr("cy", 200)
.attr("r", 3)
.attr("fill", colors.red)
.transition().duration(400)
.attr("cx", 100).attr("cy", 180 + i * 20)
.style("opacity", 0)
.remove();
}, i * 200);
}
break;
case 2: // Firewall breach
// Left: Breach through firewall
leftAttacker.transition().duration(800)
.attr("transform", "translate(100, 310)");
leftAttackPath.append("path")
.attr("d", "M85,310 Q90,310 95,310")
.attr("fill", "none")
.attr("stroke", colors.red)
.attr("stroke-width", 3)
.attr("stroke-dasharray", "8,4");
leftStatusText.text("Status: BREACH DETECTED!")
.attr("fill", colors.red);
// Right: Blocked at boundary
rightAttacker.transition().duration(800)
.attr("transform", "translate(90, 200)");
showBlockedIndicator(rightPanel, 110, 190);
rightStatusText.text("Status: Attempt Blocked")
.attr("fill", colors.green);
break;
case 3: // Inside perimeter
// Left: Attacker inside, moves freely
leftAttacker.transition().duration(600)
.attr("transform", "translate(160, 310)");
leftAttackPath.append("line")
.attr("x1", 100).attr("y1", 310)
.attr("x2", 160).attr("y2", 310)
.attr("stroke", colors.red)
.attr("stroke-width", 2)
.attr("marker-end", "url(#attackArrow)");
// Highlight all nodes as "accessible"
leftNodeGroup.selectAll("circle")
.transition().duration(500)
.attr("stroke", colors.red)
.attr("stroke-width", 3);
// Right: Still blocked, trying checkpoint
rightAttacker.transition().duration(600)
.attr("transform", "translate(130, 200)");
// Flash checkpoint verification
rightCheckpointGroup.select(".checkpoint-0 rect")
.transition().duration(200)
.attr("fill", colors.red)
.transition().duration(200)
.attr("fill", colors.orange);
showBlockedIndicator(rightPanel, 170, 175);
break;
case 4: // Lateral movement
// Left: Free lateral movement
const leftPath = [
{ x: 160, y: 270 },
{ x: 290, y: 270 },
{ x: 290, y: 350 },
{ x: 160, y: 350 }
];
leftPath.forEach((pos, i) => {
setTimeout(() => {
leftAttacker.transition().duration(400)
.attr("transform", `translate(${pos.x}, ${pos.y})`);
if (i > 0) {
leftAttackPath.append("line")
.attr("x1", leftPath[i-1].x).attr("y1", leftPath[i-1].y)
.attr("x2", pos.x).attr("y2", pos.y)
.attr("stroke", colors.red)
.attr("stroke-width", 2)
.attr("stroke-dasharray", "4,2");
}
}, i * 500);
});
leftStatusText.text("Status: LATERAL MOVEMENT!")
.attr("fill", colors.red);
// Right: Each attempt blocked
setTimeout(() => {
showBlockedIndicator(rightPanel, 145, 240);
}, 500);
setTimeout(() => {
showBlockedIndicator(rightPanel, 220, 300);
}, 1000);
rightStatusText.text("Status: All Attempts Blocked")
.attr("fill", colors.green);
break;
case 5: // Data exfiltration
// Left: Access database, exfiltrate
leftAttacker.transition().duration(500)
.attr("transform", "translate(160, 350)");
// Show data being stolen
for (let i = 0; i < 5; i++) {
setTimeout(() => {
leftAttackPath.append("circle")
.attr("cx", 160).attr("cy", 350)
.attr("r", 4)
.attr("fill", colors.orange)
.transition().duration(600)
.attr("cx", 40).attr("cy", 310)
.style("opacity", 0)
.remove();
}, i * 150);
}
leftStatusText.text("Status: DATA BREACH!")
.attr("fill", colors.red);
// Update left panel visual
leftPanel.select(".node-Database circle")
.transition().duration(300)
.attr("fill", colors.red);
// Right: Attacker contained, no access
rightStatusText.text("Status: Attack Contained")
.attr("fill", colors.green);
// Show containment visualization
rightPanel.append("circle")
.attr("cx", 130).attr("cy", 200)
.attr("r", 0)
.attr("fill", "none")
.attr("stroke", colors.green)
.attr("stroke-width", 2)
.attr("stroke-dasharray", "5,3")
.transition().duration(500)
.attr("r", 35);
break;
case 6: // Final state - comparison
// Summary boxes
const leftSummary = leftPanel.append("g")
.attr("transform", "translate(225, 130)")
.style("opacity", 0);
leftSummary.append("rect")
.attr("x", -100).attr("y", -25)
.attr("width", 200).attr("height", 50)
.attr("fill", colors.red)
.attr("rx", 6)
.attr("opacity", 0.9);
leftSummary.append("text")
.attr("text-anchor", "middle")
.attr("y", -5)
.attr("font-size", "12px")
.attr("fill", colors.white)
.attr("font-weight", "bold")
.text("COMPROMISED");
leftSummary.append("text")
.attr("text-anchor", "middle")
.attr("y", 12)
.attr("font-size", "10px")
.attr("fill", colors.white)
.text("Full network access, data stolen");
leftSummary.transition().duration(500).style("opacity", 1);
const rightSummary = rightPanel.append("g")
.attr("transform", "translate(225, 95)")
.style("opacity", 0);
rightSummary.append("rect")
.attr("x", -100).attr("y", -25)
.attr("width", 200).attr("height", 50)
.attr("fill", colors.green)
.attr("rx", 6)
.attr("opacity", 0.9);
rightSummary.append("text")
.attr("text-anchor", "middle")
.attr("y", -5)
.attr("font-size", "12px")
.attr("fill", colors.white)
.attr("font-weight", "bold")
.text("PROTECTED");
rightSummary.append("text")
.attr("text-anchor", "middle")
.attr("y", 12)
.attr("font-size", "10px")
.attr("fill", colors.white)
.text("Attack contained at first segment");
rightSummary.transition().duration(500).style("opacity", 1);
break;
}
}
function resetAnimation() {
currentPhase = 0;
isPlaying = false;
if (animationTimer) clearInterval(animationTimer);
// Reset attackers
leftAttacker.attr("transform", "translate(40, 310)").style("opacity", 0);
rightAttacker.attr("transform", "translate(40, 200)").style("opacity", 0);
// Clear attack paths
leftAttackPath.selectAll("*").remove();
rightAttackPath.selectAll("*").remove();
// Reset node colors
leftNodeGroup.selectAll("circle")
.attr("fill", colors.teal)
.attr("stroke", colors.navy)
.attr("stroke-width", 2);
rightNodeGroup.selectAll("circle")
.attr("fill", colors.teal)
.attr("stroke", colors.navy)
.attr("stroke-width", 2);
// Reset checkpoints
rightCheckpointGroup.selectAll("rect")
.attr("fill", colors.orange);
// Reset status
leftStatusText.text("Status: Secure").attr("fill", colors.gray);
rightStatusText.text("Status: Secure").attr("fill", colors.gray);
// Remove blocked indicators
leftPanel.selectAll(".blocked-indicator").remove();
rightPanel.selectAll(".blocked-indicator").remove();
// Remove summary boxes
leftPanel.selectAll("g").filter(function() {
return d3.select(this).attr("transform") === "translate(225, 130)";
}).remove();
rightPanel.selectAll("g").filter(function() {
return d3.select(this).attr("transform") === "translate(225, 95)";
}).remove();
// Remove containment circle
rightPanel.selectAll("circle").filter(function() {
return d3.select(this).attr("cx") == 130 && d3.select(this).attr("cy") == 200;
}).remove();
updatePhaseDisplay();
playBtn.text("Play Attack");
}
function playAnimation() {
if (isPlaying) {
isPlaying = false;
if (animationTimer) clearInterval(animationTimer);
playBtn.text("Resume");
return;
}
isPlaying = true;
playBtn.text("Pause");
if (currentPhase === 0) {
animatePhase(0);
}
animationTimer = setInterval(() => {
if (currentPhase < phases.length - 1) {
currentPhase++;
animatePhase(currentPhase);
} else {
isPlaying = false;
if (animationTimer) clearInterval(animationTimer);
playBtn.text("Play Attack");
}
}, 2500);
}
function stepForward() {
if (animationTimer) clearInterval(animationTimer);
isPlaying = false;
playBtn.text("Play Attack");
if (currentPhase < phases.length - 1) {
currentPhase++;
animatePhase(currentPhase);
}
}
// Event handlers
playBtn.on("click", playAnimation);
stepBtn.on("click", stepForward);
resetBtn.on("click", resetAnimation);
// Initialize
updatePhaseDisplay();
// Legend
const legend = container.append("div")
.style("display", "flex")
.style("justify-content", "center")
.style("gap", "20px")
.style("margin-top", "15px")
.style("flex-wrap", "wrap")
.style("font-size", "12px");
const legendItems = [
{ color: colors.red, label: "Attacker / Breach" },
{ color: colors.teal, label: "Protected Resource" },
{ color: colors.orange, label: "Firewall / MFA Checkpoint" },
{ color: colors.green, label: "Blocked / Contained" }
];
legendItems.forEach(item => {
const legendItem = legend.append("div")
.style("display", "flex")
.style("align-items", "center")
.style("gap", "5px");
legendItem.append("div")
.style("width", "14px")
.style("height", "14px")
.style("background", item.color)
.style("border-radius", "3px");
legendItem.append("span")
.style("color", colors.navy)
.text(item.label);
});
// Key insights
const insights = container.append("div")
.style("margin-top", "20px")
.style("padding", "15px")
.style("background", colors.lightGray)
.style("border-radius", "8px")
.style("border-left", `4px solid ${colors.teal}`);
insights.append("div")
.style("font-weight", "bold")
.style("color", colors.navy)
.style("margin-bottom", "8px")
.text("Key Differences:");
const insightsList = insights.append("ul")
.style("margin", "0")
.style("padding-left", "20px")
.style("font-size", "13px")
.style("color", colors.navy);
[
"Perimeter Security: Trust is implicit once inside the firewall boundary",
"Zero Trust: Every access request requires verification regardless of location",
"Perimeter Security: Single point of failure enables full network compromise",
"Zero Trust: Micro-segmentation contains breaches to smallest possible scope"
].forEach(text => {
insightsList.append("li")
.style("margin-bottom", "4px")
.text(text);
});
return container.node();
}1376.2 Key Concepts
1376.2.1 Perimeter Security (Castle and Moat)
Traditional perimeter security relies on a strong outer boundary (firewall) to keep attackers out. Once inside, users and systems are implicitly trusted:
- Single trust boundary: Firewall is the primary defense
- Implicit trust: Internal network traffic is assumed safe
- Flat network: Resources can communicate freely
- Vulnerability: If perimeter is breached, attacker has wide access
1376.2.2 Zero Trust Architecture
Zero Trust operates on the principle “never trust, always verify”:
- No implicit trust: Every request must be authenticated and authorized
- Micro-segmentation: Network divided into small, isolated segments
- Continuous verification: Identity and context checked at every access
- Least privilege: Users only get access to what they need
1376.2.3 IoT Security Implications
For IoT deployments, Zero Trust is increasingly important:
- Device diversity: IoT devices vary in security capabilities
- Attack surface: Large number of endpoints increases risk
- Lateral movement: Compromised devices shouldn’t access entire network
- Remote access: Many IoT devices accessed from outside traditional perimeters