1461 Network Segmentation Visualizer
Design Secure Network Zones
1461.1 Network Segmentation Visualizer
Design your own segmented network architecture, configure firewall rules, and test security with attack simulations. This interactive tool helps you understand how proper network segmentation contains breaches and protects critical assets.
This visualizer allows you to:
- Assign devices to network zones using the dropdown selectors
- Configure firewall rules between zones to control traffic flow
- Simulate attacks to see how segmentation contains breaches
- Evaluate security with an automated security score
- Step 1: Assign devices from the palette to appropriate network zones
- Step 2: Configure firewall rules to allow or deny traffic between zones
- Step 3: Click “Simulate Attack” to test your design
- Step 4: Review the security score and recommendations
- Step 5: Adjust your design and re-test to improve security
Show code
// ============================================
// Network Segmentation Visualizer
// Interactive Tool for IoT Security Education
// ============================================
{
// IEEE Color palette
const colors = {
navy: "#2C3E50",
teal: "#16A085",
orange: "#E67E22",
gray: "#7F8C8D",
lightGray: "#ECF0F1",
white: "#FFFFFF",
red: "#E74C3C",
green: "#27AE60",
purple: "#9B59B6",
yellow: "#F1C40F",
darkRed: "#C0392B",
darkTeal: "#1ABC9C",
lightBlue: "#3498DB",
darkBlue: "#1A5276"
};
// Layout configuration
const width = 950;
const height = 800;
// Network zones definition
const zones = [
{ id: "internet", name: "Internet", color: colors.red, x: 425, y: 30, width: 100, height: 60, description: "External network (untrusted)" },
{ id: "dmz", name: "DMZ", color: colors.orange, x: 350, y: 130, width: 250, height: 100, description: "Demilitarized zone for public services" },
{ id: "corporate", name: "Corporate", color: colors.teal, x: 50, y: 280, width: 250, height: 120, description: "Business workstations and servers" },
{ id: "iot", name: "IoT VLAN", color: colors.purple, x: 350, y: 280, width: 250, height: 120, description: "IoT devices isolated from corporate" },
{ id: "management", name: "Management", color: colors.darkBlue, x: 650, y: 280, width: 250, height: 120, description: "Admin access and monitoring" }
];
// Device palette - devices users can assign
const devicePalette = [
{ id: "sensor1", type: "sensor", name: "Temperature Sensor", icon: "thermometer", zone: null },
{ id: "sensor2", type: "sensor", name: "Motion Detector", icon: "motion", zone: null },
{ id: "camera1", type: "camera", name: "IP Camera", icon: "camera", zone: null },
{ id: "gateway1", type: "gateway", name: "IoT Gateway", icon: "gateway", zone: null },
{ id: "webserver", type: "server", name: "Web Server", icon: "server", zone: null },
{ id: "dbserver", type: "server", name: "Database Server", icon: "database", zone: null },
{ id: "workstation1", type: "workstation", name: "Workstation", icon: "computer", zone: null },
{ id: "workstation2", type: "workstation", name: "Admin PC", icon: "computer", zone: null },
{ id: "firewall1", type: "firewall", name: "Perimeter FW", icon: "firewall", zone: null },
{ id: "firewall2", type: "firewall", name: "Internal FW", icon: "firewall", zone: null }
];
// State management
let devices = JSON.parse(JSON.stringify(devicePalette));
let firewallRules = [];
let attackState = { active: false, compromised: [], blocked: [], phase: 0 };
let securityScore = 0;
// Create main container
const container = d3.create("div")
.style("font-family", "system-ui, -apple-system, sans-serif")
.style("max-width", "950px")
.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("Network Segmentation Visualizer");
// Main layout: 3 columns
const mainLayout = container.append("div")
.style("display", "grid")
.style("grid-template-columns", "200px 1fr 200px")
.style("gap", "15px")
.style("margin-bottom", "15px");
// === LEFT PANEL: Device Palette ===
const leftPanel = mainLayout.append("div")
.style("background", colors.lightGray)
.style("padding", "15px")
.style("border-radius", "8px");
leftPanel.append("div")
.style("font-weight", "bold")
.style("color", colors.navy)
.style("margin-bottom", "10px")
.style("font-size", "14px")
.text("Device Assignment");
const deviceList = leftPanel.append("div")
.attr("class", "device-list")
.style("display", "flex")
.style("flex-direction", "column")
.style("gap", "8px");
// === CENTER PANEL: Network Diagram ===
const centerPanel = mainLayout.append("div");
const svg = centerPanel.append("svg")
.attr("viewBox", `0 0 ${width} 450`)
.attr("width", "100%")
.style("background", colors.white)
.style("border", `2px solid ${colors.gray}`)
.style("border-radius", "8px");
// Definitions
const defs = svg.append("defs");
// Glow filter for compromised
const glowFilter = defs.append("filter")
.attr("id", "compromisedGlow")
.attr("x", "-50%")
.attr("y", "-50%")
.attr("width", "200%")
.attr("height", "200%");
glowFilter.append("feGaussianBlur")
.attr("stdDeviation", "4")
.attr("result", "coloredBlur");
const glowMerge = glowFilter.append("feMerge");
glowMerge.append("feMergeNode").attr("in", "coloredBlur");
glowMerge.append("feMergeNode").attr("in", "SourceGraphic");
// Arrow markers
defs.append("marker")
.attr("id", "allowArrow")
.attr("viewBox", "0 0 10 10")
.attr("refX", 9)
.attr("refY", 5)
.attr("markerWidth", 5)
.attr("markerHeight", 5)
.attr("orient", "auto")
.append("path")
.attr("d", "M 0 0 L 10 5 L 0 10 z")
.attr("fill", colors.green);
defs.append("marker")
.attr("id", "denyArrow")
.attr("viewBox", "0 0 10 10")
.attr("refX", 9)
.attr("refY", 5)
.attr("markerWidth", 5)
.attr("markerHeight", 5)
.attr("orient", "auto")
.append("path")
.attr("d", "M 0 0 L 10 5 L 0 10 z")
.attr("fill", colors.red);
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.darkRed);
// Draw zones
const zonesGroup = svg.append("g").attr("class", "zones");
zones.forEach(zone => {
const g = zonesGroup.append("g").attr("class", `zone-${zone.id}`);
// Zone background
g.append("rect")
.attr("x", zone.x)
.attr("y", zone.y)
.attr("width", zone.width)
.attr("height", zone.height)
.attr("fill", zone.color)
.attr("opacity", 0.15)
.attr("stroke", zone.color)
.attr("stroke-width", 2)
.attr("stroke-dasharray", zone.id === "internet" ? "none" : "5,3")
.attr("rx", 8);
// Zone label
g.append("text")
.attr("x", zone.x + zone.width / 2)
.attr("y", zone.y + 18)
.attr("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold")
.attr("fill", zone.color)
.text(zone.name);
// Zone description
g.append("text")
.attr("x", zone.x + zone.width / 2)
.attr("y", zone.y + zone.height - 8)
.attr("text-anchor", "middle")
.attr("font-size", "9px")
.attr("fill", colors.gray)
.text(zone.description);
});
// Network connections (visual)
const connectionsGroup = svg.append("g").attr("class", "connections");
// Internet to DMZ
connectionsGroup.append("line")
.attr("x1", 475).attr("y1", 90)
.attr("x2", 475).attr("y2", 130)
.attr("stroke", colors.gray)
.attr("stroke-width", 2)
.attr("stroke-dasharray", "4,2");
// DMZ to other zones
connectionsGroup.append("line")
.attr("x1", 400).attr("y1", 230)
.attr("x2", 175).attr("y2", 280)
.attr("stroke", colors.gray)
.attr("stroke-width", 2)
.attr("stroke-dasharray", "4,2");
connectionsGroup.append("line")
.attr("x1", 475).attr("y1", 230)
.attr("x2", 475).attr("y2", 280)
.attr("stroke", colors.gray)
.attr("stroke-width", 2)
.attr("stroke-dasharray", "4,2");
connectionsGroup.append("line")
.attr("x1", 550).attr("y1", 230)
.attr("x2", 775).attr("y2", 280)
.attr("stroke", colors.gray)
.attr("stroke-width", 2)
.attr("stroke-dasharray", "4,2");
// Devices in zones group
const devicesGroup = svg.append("g").attr("class", "devices-in-zones");
// Traffic flow visualization group
const trafficGroup = svg.append("g").attr("class", "traffic-flow");
// Attack visualization group
const attackGroup = svg.append("g").attr("class", "attack-vis");
// === RIGHT PANEL: Firewall Rules ===
const rightPanel = mainLayout.append("div")
.style("background", colors.lightGray)
.style("padding", "15px")
.style("border-radius", "8px");
rightPanel.append("div")
.style("font-weight", "bold")
.style("color", colors.navy)
.style("margin-bottom", "10px")
.style("font-size", "14px")
.text("Firewall Rules");
const ruleBuilder = rightPanel.append("div")
.style("display", "flex")
.style("flex-direction", "column")
.style("gap", "8px");
// Source zone selector
ruleBuilder.append("label")
.style("font-size", "11px")
.style("color", colors.gray)
.text("Source Zone:");
const sourceSelect = ruleBuilder.append("select")
.attr("class", "source-zone-select")
.style("padding", "6px")
.style("border-radius", "4px")
.style("border", `1px solid ${colors.gray}`)
.style("font-size", "12px");
sourceSelect.append("option").attr("value", "").text("Select source...");
zones.forEach(z => {
sourceSelect.append("option").attr("value", z.id).text(z.name);
});
// Destination zone selector
ruleBuilder.append("label")
.style("font-size", "11px")
.style("color", colors.gray)
.style("margin-top", "5px")
.text("Destination Zone:");
const destSelect = ruleBuilder.append("select")
.attr("class", "dest-zone-select")
.style("padding", "6px")
.style("border-radius", "4px")
.style("border", `1px solid ${colors.gray}`)
.style("font-size", "12px");
destSelect.append("option").attr("value", "").text("Select destination...");
zones.forEach(z => {
destSelect.append("option").attr("value", z.id).text(z.name);
});
// Action selector
ruleBuilder.append("label")
.style("font-size", "11px")
.style("color", colors.gray)
.style("margin-top", "5px")
.text("Action:");
const actionSelect = ruleBuilder.append("select")
.attr("class", "action-select")
.style("padding", "6px")
.style("border-radius", "4px")
.style("border", `1px solid ${colors.gray}`)
.style("font-size", "12px");
actionSelect.append("option").attr("value", "deny").text("DENY");
actionSelect.append("option").attr("value", "allow").text("ALLOW");
// Add rule button
const addRuleBtn = ruleBuilder.append("button")
.text("Add Rule")
.style("margin-top", "10px")
.style("padding", "8px 12px")
.style("background", colors.teal)
.style("color", colors.white)
.style("border", "none")
.style("border-radius", "4px")
.style("cursor", "pointer")
.style("font-size", "12px")
.style("font-weight", "bold");
// Rules list
rightPanel.append("div")
.style("margin-top", "15px")
.style("font-weight", "bold")
.style("font-size", "12px")
.style("color", colors.navy)
.text("Active Rules:");
const rulesList = rightPanel.append("div")
.attr("class", "rules-list")
.style("margin-top", "8px")
.style("max-height", "180px")
.style("overflow-y", "auto")
.style("font-size", "10px");
// Clear rules button
const clearRulesBtn = rightPanel.append("button")
.text("Clear All Rules")
.style("margin-top", "10px")
.style("padding", "6px 10px")
.style("background", colors.gray)
.style("color", colors.white)
.style("border", "none")
.style("border-radius", "4px")
.style("cursor", "pointer")
.style("font-size", "11px")
.style("width", "100%");
// === BOTTOM PANEL: Controls and Results ===
const bottomPanel = container.append("div")
.style("display", "grid")
.style("grid-template-columns", "1fr 1fr")
.style("gap", "15px");
// Control buttons
const controlsBox = bottomPanel.append("div")
.style("background", colors.navy)
.style("padding", "15px")
.style("border-radius", "8px")
.style("color", colors.white);
controlsBox.append("div")
.style("font-weight", "bold")
.style("margin-bottom", "10px")
.text("Simulation Controls");
const controlBtns = controlsBox.append("div")
.style("display", "flex")
.style("gap", "10px")
.style("flex-wrap", "wrap");
const attackBtn = controlBtns.append("button")
.text("Simulate Attack")
.style("padding", "10px 15px")
.style("background", colors.red)
.style("color", colors.white)
.style("border", "none")
.style("border-radius", "4px")
.style("cursor", "pointer")
.style("font-weight", "bold")
.style("font-size", "13px");
const trafficBtn = controlBtns.append("button")
.text("Show Traffic Flow")
.style("padding", "10px 15px")
.style("background", colors.teal)
.style("color", colors.white)
.style("border", "none")
.style("border-radius", "4px")
.style("cursor", "pointer")
.style("font-weight", "bold")
.style("font-size", "13px");
const resetBtn = controlBtns.append("button")
.text("Reset All")
.style("padding", "10px 15px")
.style("background", colors.gray)
.style("color", colors.white)
.style("border", "none")
.style("border-radius", "4px")
.style("cursor", "pointer")
.style("font-weight", "bold")
.style("font-size", "13px");
// Attack scenario selector
controlsBox.append("div")
.style("margin-top", "12px")
.style("font-size", "12px")
.text("Attack Scenario:");
const scenarioSelect = controlsBox.append("select")
.style("margin-top", "5px")
.style("padding", "6px")
.style("border-radius", "4px")
.style("border", "none")
.style("font-size", "12px")
.style("width", "100%");
scenarioSelect.append("option").attr("value", "iot").text("Compromised IoT Device");
scenarioSelect.append("option").attr("value", "web").text("Web Server Breach");
scenarioSelect.append("option").attr("value", "phishing").text("Phishing Attack (Workstation)");
// Security score box
const scoreBox = bottomPanel.append("div")
.style("background", colors.lightGray)
.style("padding", "15px")
.style("border-radius", "8px");
scoreBox.append("div")
.style("font-weight", "bold")
.style("color", colors.navy)
.style("margin-bottom", "10px")
.text("Security Assessment");
const scoreDisplay = scoreBox.append("div")
.attr("class", "score-display")
.style("display", "flex")
.style("align-items", "center")
.style("gap", "15px");
const scoreCircle = scoreDisplay.append("div")
.style("width", "60px")
.style("height", "60px")
.style("border-radius", "50%")
.style("background", colors.gray)
.style("display", "flex")
.style("align-items", "center")
.style("justify-content", "center")
.style("color", colors.white)
.style("font-size", "18px")
.style("font-weight", "bold")
.text("0");
const scoreLabel = scoreDisplay.append("div")
.style("font-size", "12px")
.style("color", colors.gray);
scoreLabel.append("div")
.attr("class", "score-label")
.text("Not Evaluated");
scoreLabel.append("div")
.attr("class", "score-detail")
.style("font-size", "11px")
.style("margin-top", "3px")
.text("Assign devices and add rules to begin");
// Recommendations
const recommendations = scoreBox.append("div")
.attr("class", "recommendations")
.style("margin-top", "12px")
.style("font-size", "11px")
.style("color", colors.navy);
// === HELPER FUNCTIONS ===
function getDeviceIcon(type) {
const icons = {
sensor: "S",
camera: "C",
gateway: "G",
server: "Sv",
workstation: "W",
firewall: "FW",
database: "DB"
};
return icons[type] || "?";
}
function getDeviceColor(type) {
const deviceColors = {
sensor: colors.orange,
camera: colors.orange,
gateway: colors.purple,
server: colors.teal,
workstation: colors.lightBlue,
firewall: colors.red,
database: colors.darkTeal
};
return deviceColors[type] || colors.gray;
}
function renderDeviceList() {
deviceList.selectAll("*").remove();
devices.forEach((device, i) => {
const item = deviceList.append("div")
.style("display", "flex")
.style("flex-direction", "column")
.style("gap", "4px")
.style("padding", "8px")
.style("background", colors.white)
.style("border-radius", "4px")
.style("border-left", `3px solid ${getDeviceColor(device.type)}`);
item.append("div")
.style("font-size", "11px")
.style("font-weight", "bold")
.style("color", colors.navy)
.text(device.name);
const select = item.append("select")
.style("padding", "4px")
.style("font-size", "10px")
.style("border-radius", "3px")
.style("border", `1px solid ${colors.gray}`)
.on("change", function() {
const newZone = this.value || null;
devices[i].zone = newZone;
renderDevicesInZones();
calculateSecurityScore();
});
select.append("option")
.attr("value", "")
.text("-- Unassigned --");
zones.forEach(zone => {
select.append("option")
.attr("value", zone.id)
.attr("selected", device.zone === zone.id ? true : null)
.text(zone.name);
});
});
}
function renderDevicesInZones() {
devicesGroup.selectAll("*").remove();
// Group devices by zone
const devicesByZone = {};
zones.forEach(z => devicesByZone[z.id] = []);
devices.forEach(d => {
if (d.zone && devicesByZone[d.zone]) {
devicesByZone[d.zone].push(d);
}
});
// Render devices in each zone
zones.forEach(zone => {
const zoneDevices = devicesByZone[zone.id];
const cols = Math.min(zoneDevices.length, 4);
const rows = Math.ceil(zoneDevices.length / cols);
zoneDevices.forEach((device, i) => {
const col = i % cols;
const row = Math.floor(i / cols);
const spacing = zone.width / (cols + 1);
const vSpacing = (zone.height - 40) / (rows + 1);
const x = zone.x + spacing * (col + 1);
const y = zone.y + 30 + vSpacing * (row + 1);
const g = devicesGroup.append("g")
.attr("class", `device-${device.id}`)
.attr("transform", `translate(${x - 15}, ${y - 15})`);
g.append("rect")
.attr("width", 30)
.attr("height", 30)
.attr("rx", 4)
.attr("fill", getDeviceColor(device.type))
.attr("stroke", colors.navy)
.attr("stroke-width", 1.5);
g.append("text")
.attr("x", 15)
.attr("y", 18)
.attr("text-anchor", "middle")
.attr("font-size", "10px")
.attr("font-weight", "bold")
.attr("fill", colors.white)
.text(getDeviceIcon(device.type));
// Tooltip
g.append("title")
.text(`${device.name}\nZone: ${zone.name}`);
});
});
}
function renderRulesList() {
rulesList.selectAll("*").remove();
if (firewallRules.length === 0) {
rulesList.append("div")
.style("color", colors.gray)
.style("font-style", "italic")
.style("padding", "5px")
.text("No rules configured. Default: DENY ALL");
return;
}
firewallRules.forEach((rule, i) => {
const sourceZone = zones.find(z => z.id === rule.source);
const destZone = zones.find(z => z.id === rule.dest);
const item = rulesList.append("div")
.style("display", "flex")
.style("justify-content", "space-between")
.style("align-items", "center")
.style("padding", "4px 6px")
.style("margin-bottom", "3px")
.style("background", rule.action === "allow" ? "#d5f5e3" : "#fadbd8")
.style("border-radius", "3px");
item.append("span")
.style("font-size", "9px")
.html(`${sourceZone?.name || rule.source} → ${destZone?.name || rule.dest}: <b>${rule.action.toUpperCase()}</b>`);
item.append("button")
.text("X")
.style("background", "transparent")
.style("border", "none")
.style("color", colors.gray)
.style("cursor", "pointer")
.style("font-size", "10px")
.style("padding", "2px 5px")
.on("click", () => {
firewallRules.splice(i, 1);
renderRulesList();
calculateSecurityScore();
});
});
}
function addRule() {
const source = sourceSelect.node().value;
const dest = destSelect.node().value;
const action = actionSelect.node().value;
if (!source || !dest) {
alert("Please select both source and destination zones.");
return;
}
if (source === dest) {
alert("Source and destination cannot be the same zone.");
return;
}
// Check for duplicate
const existing = firewallRules.find(r => r.source === source && r.dest === dest);
if (existing) {
existing.action = action;
} else {
firewallRules.push({ source, dest, action });
}
renderRulesList();
calculateSecurityScore();
// Reset selects
sourceSelect.node().value = "";
destSelect.node().value = "";
}
function isTrafficAllowed(source, dest) {
// Check explicit rules
const rule = firewallRules.find(r => r.source === source && r.dest === dest);
if (rule) return rule.action === "allow";
// Default: deny all inter-zone traffic (except to internet for responses)
return false;
}
function calculateSecurityScore() {
let score = 0;
let maxScore = 100;
let issues = [];
let positives = [];
// Check device assignments
const assignedDevices = devices.filter(d => d.zone);
const unassignedDevices = devices.filter(d => !d.zone);
if (assignedDevices.length === 0) {
scoreCircle.style("background", colors.gray).text("--");
scoreLabel.select(".score-label").text("No Devices Assigned");
scoreLabel.select(".score-detail").text("Assign devices to zones to begin");
recommendations.html("");
return;
}
// +10 points for each device properly assigned
const properAssignments = {
sensor: ["iot"],
camera: ["iot", "dmz"],
gateway: ["iot", "dmz"],
server: ["dmz", "corporate"],
workstation: ["corporate"],
firewall: ["dmz", "management"],
database: ["corporate"]
};
devices.forEach(d => {
if (d.zone) {
const proper = properAssignments[d.type] || [];
if (proper.includes(d.zone)) {
score += 8;
positives.push(`${d.name} properly placed in ${zones.find(z => z.id === d.zone)?.name}`);
} else {
score += 3;
issues.push(`${d.name} may be misplaced in ${zones.find(z => z.id === d.zone)?.name}`);
}
}
});
// Check firewall rules
// +15 points for blocking internet to corporate
if (firewallRules.some(r => r.source === "internet" && r.dest === "corporate" && r.action === "deny")) {
score += 15;
positives.push("Internet to Corporate blocked");
} else if (!firewallRules.some(r => r.source === "internet" && r.dest === "corporate" && r.action === "allow")) {
score += 10; // Default deny is good
}
// +10 points for blocking IoT to Corporate
if (firewallRules.some(r => r.source === "iot" && r.dest === "corporate" && r.action === "deny")) {
score += 10;
positives.push("IoT to Corporate blocked");
} else if (firewallRules.some(r => r.source === "iot" && r.dest === "corporate" && r.action === "allow")) {
score -= 10;
issues.push("IoT has access to Corporate (risky)");
}
// +10 for blocking IoT to Management
if (firewallRules.some(r => r.source === "iot" && r.dest === "management" && r.action === "deny")) {
score += 10;
positives.push("IoT to Management blocked");
} else if (firewallRules.some(r => r.source === "iot" && r.dest === "management" && r.action === "allow")) {
score -= 15;
issues.push("IoT has access to Management (critical risk)");
}
// +5 for having any firewall rules
if (firewallRules.length > 0) {
score += 5;
} else {
issues.push("No explicit firewall rules configured");
}
// Penalty for allowing internet to internal zones
if (firewallRules.some(r => r.source === "internet" && r.action === "allow" && r.dest !== "dmz")) {
score -= 20;
issues.push("Internet has direct access to internal zones!");
}
// Bonus for Management isolation
const mgmtAllowed = firewallRules.filter(r => r.dest === "management" && r.action === "allow");
if (mgmtAllowed.length === 0 || mgmtAllowed.every(r => r.source === "corporate")) {
score += 10;
positives.push("Management zone well isolated");
}
// Normalize score
score = Math.max(0, Math.min(100, score));
securityScore = score;
// Update display
let scoreColor = colors.red;
let label = "Critical";
if (score >= 80) {
scoreColor = colors.green;
label = "Excellent";
} else if (score >= 60) {
scoreColor = colors.teal;
label = "Good";
} else if (score >= 40) {
scoreColor = colors.orange;
label = "Fair";
} else if (score >= 20) {
scoreColor = colors.yellow;
label = "Poor";
}
scoreCircle
.style("background", scoreColor)
.text(score);
scoreLabel.select(".score-label")
.text(label)
.style("color", scoreColor)
.style("font-weight", "bold");
scoreLabel.select(".score-detail")
.text(`${assignedDevices.length}/${devices.length} devices assigned, ${firewallRules.length} rules`);
// Recommendations
recommendations.html("");
if (issues.length > 0) {
recommendations.append("div")
.style("color", colors.red)
.style("margin-bottom", "5px")
.html("<b>Issues:</b>");
issues.slice(0, 3).forEach(issue => {
recommendations.append("div")
.style("padding-left", "8px")
.style("margin-bottom", "2px")
.text("- " + issue);
});
}
if (positives.length > 0 && issues.length < 3) {
recommendations.append("div")
.style("color", colors.green)
.style("margin-top", "5px")
.html("<b>Strengths:</b>");
positives.slice(0, 2).forEach(pos => {
recommendations.append("div")
.style("padding-left", "8px")
.style("margin-bottom", "2px")
.text("- " + pos);
});
}
}
function simulateAttack() {
if (attackState.active) return;
const scenario = scenarioSelect.node().value;
attackState = { active: true, compromised: [], blocked: [], phase: 0 };
attackGroup.selectAll("*").remove();
attackBtn.text("Attack in Progress...")
.style("background", colors.darkRed);
// Determine starting point based on scenario
let startZone, startDevice;
switch (scenario) {
case "iot":
startZone = "iot";
startDevice = devices.find(d => d.zone === "iot" && d.type === "sensor");
break;
case "web":
startZone = "dmz";
startDevice = devices.find(d => d.zone === "dmz" && d.type === "server");
break;
case "phishing":
startZone = "corporate";
startDevice = devices.find(d => d.zone === "corporate" && d.type === "workstation");
break;
}
if (!startDevice) {
alert(`No suitable device found in ${startZone} zone for this attack scenario. Please assign devices first.`);
resetAttack();
return;
}
// Mark initial compromise
attackState.compromised.push(startDevice.id);
highlightCompromised(startDevice);
// Show attacker icon
const targetZone = zones.find(z => z.id === startZone);
attackGroup.append("circle")
.attr("cx", targetZone.x - 30)
.attr("cy", targetZone.y + targetZone.height / 2)
.attr("r", 15)
.attr("fill", colors.red)
.attr("stroke", colors.darkRed)
.attr("stroke-width", 2)
.style("opacity", 0)
.transition()
.duration(500)
.style("opacity", 1);
attackGroup.append("text")
.attr("x", targetZone.x - 30)
.attr("y", targetZone.y + targetZone.height / 2 + 4)
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.attr("font-weight", "bold")
.attr("fill", colors.white)
.text("!");
// Attempt lateral movement
setTimeout(() => {
attemptLateralMovement(startZone, startDevice);
}, 1500);
}
function attemptLateralMovement(currentZone, fromDevice) {
const targetZones = zones.filter(z => z.id !== currentZone && z.id !== "internet");
let attemptIndex = 0;
function tryNextZone() {
if (attemptIndex >= targetZones.length) {
// Attack complete
setTimeout(finishAttack, 1000);
return;
}
const targetZone = targetZones[attemptIndex];
attemptIndex++;
// Check if traffic is allowed
const allowed = isTrafficAllowed(currentZone, targetZone.id);
const sourceZoneObj = zones.find(z => z.id === currentZone);
const sourceX = sourceZoneObj.x + sourceZoneObj.width / 2;
const sourceY = sourceZoneObj.y + sourceZoneObj.height / 2;
const targetX = targetZone.x + targetZone.width / 2;
const targetY = targetZone.y + targetZone.height / 2;
// Draw attack arrow
const line = attackGroup.append("line")
.attr("x1", sourceX)
.attr("y1", sourceY)
.attr("x2", sourceX)
.attr("y2", sourceY)
.attr("stroke", allowed ? colors.red : colors.orange)
.attr("stroke-width", 2)
.attr("stroke-dasharray", allowed ? "none" : "5,3")
.attr("marker-end", "url(#attackArrow)");
line.transition()
.duration(600)
.attr("x2", allowed ? targetX : (sourceX + targetX) / 2)
.attr("y2", allowed ? targetY : (sourceY + targetY) / 2)
.on("end", () => {
if (allowed) {
// Compromise devices in target zone
const targetDevices = devices.filter(d => d.zone === targetZone.id && !attackState.compromised.includes(d.id));
targetDevices.forEach(d => {
attackState.compromised.push(d.id);
highlightCompromised(d);
});
// Show spread indicator
attackGroup.append("text")
.attr("x", targetX)
.attr("y", targetY - 40)
.attr("text-anchor", "middle")
.attr("font-size", "10px")
.attr("fill", colors.red)
.attr("font-weight", "bold")
.text("COMPROMISED!")
.transition()
.duration(500)
.attr("y", targetY - 50)
.style("opacity", 0.7);
} else {
// Show blocked indicator
attackState.blocked.push(targetZone.id);
const blockX = (sourceX + targetX) / 2;
const blockY = (sourceY + targetY) / 2;
attackGroup.append("circle")
.attr("cx", blockX)
.attr("cy", blockY)
.attr("r", 12)
.attr("fill", "none")
.attr("stroke", colors.green)
.attr("stroke-width", 3);
attackGroup.append("line")
.attr("x1", blockX - 8)
.attr("y1", blockY - 8)
.attr("x2", blockX + 8)
.attr("y2", blockY + 8)
.attr("stroke", colors.green)
.attr("stroke-width", 3);
attackGroup.append("text")
.attr("x", blockX)
.attr("y", blockY + 25)
.attr("text-anchor", "middle")
.attr("font-size", "8px")
.attr("fill", colors.green)
.attr("font-weight", "bold")
.text("BLOCKED");
}
setTimeout(tryNextZone, 800);
});
}
tryNextZone();
}
function highlightCompromised(device) {
const deviceEl = devicesGroup.select(`.device-${device.id}`);
if (!deviceEl.empty()) {
deviceEl.select("rect")
.transition()
.duration(300)
.attr("fill", colors.red)
.attr("stroke", colors.darkRed)
.attr("filter", "url(#compromisedGlow)");
}
}
function finishAttack() {
attackState.active = false;
attackBtn.text("Simulate Attack")
.style("background", colors.red);
// Show summary
const total = devices.filter(d => d.zone).length;
const compromised = attackState.compromised.length;
const blocked = attackState.blocked.length;
const summary = attackGroup.append("g")
.attr("transform", `translate(${width / 2}, 420)`);
summary.append("rect")
.attr("x", -150)
.attr("y", -25)
.attr("width", 300)
.attr("height", 50)
.attr("fill", colors.navy)
.attr("rx", 6)
.style("opacity", 0.95);
summary.append("text")
.attr("text-anchor", "middle")
.attr("y", -5)
.attr("font-size", "12px")
.attr("fill", colors.white)
.attr("font-weight", "bold")
.text(`Attack Result: ${compromised}/${total} devices compromised`);
summary.append("text")
.attr("text-anchor", "middle")
.attr("y", 15)
.attr("font-size", "11px")
.attr("fill", blocked > 0 ? colors.green : colors.red)
.text(blocked > 0 ? `${blocked} zones protected by firewall rules` : "No zones were protected!");
}
function resetAttack() {
attackState = { active: false, compromised: [], blocked: [], phase: 0 };
attackGroup.selectAll("*").remove();
attackBtn.text("Simulate Attack")
.style("background", colors.red);
// Reset device colors
renderDevicesInZones();
}
function showTrafficFlow() {
trafficGroup.selectAll("*").remove();
if (firewallRules.length === 0) {
alert("No firewall rules configured. Add rules to visualize traffic flow.");
return;
}
firewallRules.forEach((rule, i) => {
const sourceZone = zones.find(z => z.id === rule.source);
const destZone = zones.find(z => z.id === rule.dest);
if (!sourceZone || !destZone) return;
const x1 = sourceZone.x + sourceZone.width / 2;
const y1 = sourceZone.y + sourceZone.height / 2;
const x2 = destZone.x + destZone.width / 2;
const y2 = destZone.y + destZone.height / 2;
// Offset slightly to prevent overlap
const offset = (i - firewallRules.length / 2) * 8;
trafficGroup.append("line")
.attr("x1", x1 + offset)
.attr("y1", y1)
.attr("x2", x2 + offset)
.attr("y2", y2)
.attr("stroke", rule.action === "allow" ? colors.green : colors.red)
.attr("stroke-width", 2)
.attr("stroke-dasharray", rule.action === "allow" ? "none" : "5,3")
.attr("marker-end", rule.action === "allow" ? "url(#allowArrow)" : "url(#denyArrow)")
.style("opacity", 0)
.transition()
.delay(i * 200)
.duration(400)
.style("opacity", 0.7);
});
// Auto-clear after 5 seconds
setTimeout(() => {
trafficGroup.selectAll("*")
.transition()
.duration(500)
.style("opacity", 0)
.remove();
}, 5000);
}
function resetAll() {
devices = JSON.parse(JSON.stringify(devicePalette));
firewallRules = [];
attackState = { active: false, compromised: [], blocked: [], phase: 0 };
renderDeviceList();
renderDevicesInZones();
renderRulesList();
calculateSecurityScore();
attackGroup.selectAll("*").remove();
trafficGroup.selectAll("*").remove();
}
// === EVENT HANDLERS ===
addRuleBtn.on("click", addRule);
clearRulesBtn.on("click", () => {
firewallRules = [];
renderRulesList();
calculateSecurityScore();
});
attackBtn.on("click", () => {
resetAttack();
setTimeout(simulateAttack, 100);
});
trafficBtn.on("click", showTrafficFlow);
resetBtn.on("click", resetAll);
// === INITIAL RENDER ===
renderDeviceList();
renderDevicesInZones();
renderRulesList();
calculateSecurityScore();
// 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", "11px");
const legendItems = [
{ color: colors.orange, label: "Sensors/Cameras" },
{ color: colors.purple, label: "Gateways" },
{ color: colors.teal, label: "Servers" },
{ color: colors.lightBlue, label: "Workstations" },
{ color: colors.red, label: "Firewalls" },
{ color: colors.green, border: true, label: "ALLOW rule" },
{ color: colors.red, border: true, label: "DENY rule" }
];
legendItems.forEach(item => {
const legendItem = legend.append("div")
.style("display", "flex")
.style("align-items", "center")
.style("gap", "4px");
if (item.border) {
legendItem.append("div")
.style("width", "20px")
.style("height", "2px")
.style("background", item.color);
} else {
legendItem.append("div")
.style("width", "12px")
.style("height", "12px")
.style("background", item.color)
.style("border-radius", "2px");
}
legendItem.append("span")
.style("color", colors.navy)
.text(item.label);
});
return container.node();
}1461.2 Understanding Network Segmentation Design
This interactive tool demonstrates key network segmentation principles for IoT security:
1461.2.1 Zone Architecture
| Zone | Purpose | Typical Devices | Security Level |
|---|---|---|---|
| Internet | External untrusted network | External connections | Untrusted |
| DMZ | Public-facing services | Web servers, gateways | Medium security |
| Corporate | Business operations | Workstations, databases | High security |
| IoT VLAN | IoT device isolation | Sensors, cameras | Isolated, monitored |
| Management | Administrative access | Admin PCs, monitoring | Highest security |
1461.2.2 Best Practices for Device Placement
- Sensors and cameras: Always in IoT VLAN (isolated from corporate)
- IoT Gateways: IoT VLAN or DMZ (if internet-facing)
- Web servers: DMZ (public-facing but protected)
- Database servers: Corporate (never in DMZ)
- Workstations: Corporate zone
- Firewalls: DMZ or Management (traffic inspection points)
1461.2.3 Firewall Rule Recommendations
For maximum security, configure these rules:
- DENY Internet to Corporate (critical)
- DENY Internet to Management (critical)
- DENY IoT to Corporate (high priority)
- DENY IoT to Management (high priority)
- ALLOW Corporate to DMZ (for services)
- ALLOW Management to all zones (for admin)
1461.3 Attack Scenarios Explained
1461.3.1 Compromised IoT Device
Simulates an attacker exploiting a vulnerable IoT sensor. Without proper segmentation, the attacker can pivot from the IoT network to corporate systems.
1461.3.2 Web Server Breach
Simulates a DMZ web server compromise. Proper firewall rules prevent the attacker from reaching internal zones.
1461.3.3 Phishing Attack
Simulates a compromised corporate workstation. Even with internal access, segmentation limits the attacker’s reach to the management zone.
1461.4 What’s Next
- Network Segmentation Defense Animation - See attack containment in action
- Zero Trust Policy Simulator - Design access policies
- IoT Device Security - Comprehensive device protection
- Threat Modelling - Identify risks in your architecture
This visualizer implements:
- Device assignment system: Dropdown selectors for placing devices in zones
- Firewall rule builder: Create source-destination-action rules
- Security scoring engine: Evaluates design based on best practices
- Attack simulation: Visual representation of lateral movement attempts
- Traffic flow visualization: Shows allowed vs blocked paths
- IEEE color palette: Consistent visual design
Educational simplifications:
- Zones are simplified (real networks have more granular segmentation)
- Rule evaluation is basic (real firewalls have complex ACLs)
- Attack paths are direct (real attacks use multiple techniques)