Introduction

Security metrics translate technical security operations into business-relevant information that drives decision-making. Without metrics, security programs cannot demonstrate value, identify weaknesses, or justify resource allocation. Effective security reporting addresses multiple audiences — from technical teams to the board of directors — each with different information needs.
KPIs vs KRIs
Key Performance Indicators (KPIs)
KPIs measure the efficiency and effectiveness of security operations.
class SecurityKPI:
def init(self):
self.metrics = {}
def calculate_mttd(self, detection_times):
"""Mean Time to Detect — average time from compromise to detection."""
if not detection_times:
return None
return sum(detection_times) / len(detection_times)
def calculate_mttr(self, response_times):
"""Mean Time to Respond — average time from detection to containment."""
if not response_times:
return None
return sum(response_times) / len(response_times)
def calculate_coverage_rate(self, monitored_assets, total_assets):
"""Percentage of assets under monitoring."""
if total_assets == 0:
return 0
return (monitored_assets / total_assets) * 100
def calculate_patch_compliance(self, patched_systems, vulnerable_systems):
"""Percentage of systems patched within SLA."""
total = patched_systems + vulnerable_systems
if total == 0:
return 100
return (patched_systems / total) * 100
Key Risk Indicators (KRIs)
KRIs measure the level of security risk exposure.
class SecurityKRI:
def calculate_vulnerability_risk_score(self, vulnerabilities):
"""Weighted risk score based on CVSS and asset criticality."""
total_risk = 0
for vuln in vulnerabilities:
CVSS score * asset criticality multiplier
risk = vuln['cvss'] * (vuln['asset_criticality'] / 5)
Exploit availability multiplier
if vuln.get('exploit_available'):
risk *= 1.5
if vuln.get('in_wild'):
risk *= 2.0
total_risk += risk
return total_risk
def calculate_mean_time_to_patch(self, patch_times):
"""Average time to apply security patches by severity."""
categories = {'critical': [], 'high': [], 'medium': [], 'low': []}
for patch in patch_times:
categories[patch['severity']].append(patch['hours_to_patch'])
return {
severity: sum(times) / len(times) if times else 0
for severity, times in categories.items()
}
Dashboard Design
Effective dashboards present the right information at the right level of detail for the audience.
Executive Dashboard
def generate_executive_dashboard(metrics):
"""Board-level dashboard — strategic, summary, trend-focused."""
return {
'security_posture_score': metrics['posture_score'], # 0-100
'risk_trend': metrics['risk_trend'], # improving/stable/declining
'incidents_this_quarter': {
'total': metrics['incident_count'],
'critical': metrics['critical_incidents'],
'trend': metrics['incident_trend']
},
'top_risks': [
{'risk': 'Unpatched critical vulnerabilities',
'status': 'on_track', 'due_date': '2026-06-01'},
{'risk': 'Cloud misconfigurations',
'status': 'at_risk', 'due_date': '2026-05-15'},
],
'compliance_status': {
'soc2': 'compliant',
'pci_dss': 'compliant',
'hipaa': 'in_progress'
},
'budget_utilization': {
'allocated': 2500000,
'spent': 1850000,
'remaining': 650000
}
}
Operational Dashboard
def generate_operational_dashboard(soc_metrics):
"""SOC-level dashboard — tactical, detailed, real-time."""
return {
'alert_volume': {
'today': 847,
'change': '+12%',
'by_severity': {
'critical': 3,
'high': 27,
'medium': 145,
'low': 672
}
},
'mean_times': {
'mttd': '45 minutes',
'mttr': '3.2 hours',
'triage_time': '8 minutes'
},
'queue_status': {
'unassigned': 23,
'in_progress': 45,
'escalated': 12
},
'false_positive_rate': '28%',
'coverage': {
'endpoints': '98.5%',
'servers': '100%',
'cloud_accounts': '95%'
}
}
Board Reporting Framework
quarterly_board_report:
sections:
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\- title: "Executive Summary"
content:
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\- "Security posture improved from 72 to 78 (target: 80)"
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\- "Zero critical incidents this quarter"
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\- "Three compliance audits passed"
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\- title: "Incident Summary"
metrics:
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\- name: "Total Incidents"
value: 12
trend: "decreasing"
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\- name: "Mean Time to Detect"
value: "45 min"
target: "< 60 min"
status: "on_track"
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\- name: "Mean Time to Respond"
value: "3.2 hrs"
target: "< 4 hrs"
status: "on_track"
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\- name: "Phishing Click Rate"
value: "4.2%"
target: "< 5%"
status: "on_track"
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\- title: "Risk Profile"
risks:
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\- id: "R-001"
description: "Third-party vendor access to production"
likelihood: "medium"
impact: "high"
mitigation: "Vendor access review in progress"
target_date: "2026-07-01"
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\- title: "Compliance Status"
frameworks:
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\- name: "SOC 2 Type II"
status: "compliant"
last_audit: "2026-03-15"
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\- name: "PCI DSS 4.0"
status: "compliant"
last_audit: "2026-02-28"
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\- name: "ISO 27001"
status: "in_progress"
target: "2026-09-01"
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\- title: "Resource Allocation"
budget:
allocated: "$2.5M"
spent: "$1.85M (74%)"
key_investments:
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\- "EDR platform upgrade: $450K"
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\- "Security awareness training: $85K"
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\- "Penetration testing: $120K"
Security Scorecards
def generate_team_scorecard(metrics):
"""Department-level scorecard for security team performance."""
thresholds = {
'patch_compliance': {'good': 95, 'acceptable': 85},
'coverage': {'good': 98, 'acceptable': 90},
'false_positive_rate': {'good': 20, 'acceptable': 35},
'mttd_minutes': {'good': 30, 'acceptable': 60},
'mttr_hours': {'good': 2, 'acceptable': 4},
}
scorecard = {}
for metric, value in metrics.items():
if metric not in thresholds:
continue
threshold = thresholds[metric]
if value >= threshold['good']:
scorecard[metric] = 'GREEN'
elif value >= threshold['acceptable']:
scorecard[metric] = 'YELLOW'
else:
scorecard[metric] = 'RED'
return scorecard
Industry Benchmarks
industry_benchmarks:
mttd:
top_performer: "< 1 hour"
average: "24-48 hours"
below_average: "> 1 week"
mttr:
top_performer: "< 2 hours"
average: "4-8 hours"
below_average: "> 24 hours"
vulnerability_remediation:
critical: "15 days (top), 30 days (average)"
high: "30 days (top), 60 days (average)"
security_spend:
as_percentage_of_it: "5-10% (recommended)"
per_employee: "$1,500-2,500 (enterprise)"
Conclusion
Security metrics must be meaningful, measurable, and actionable. Distinguish between KPIs that measure operational effectiveness and KRIs that measure risk exposure. Tailor dashboards for each audience — executives need strategic trends and risk summaries, while SOC teams need real-time operational data. Benchmark against industry peers, track trends over time (not just snapshots), and always tie metrics back to business impact and risk reduction.
Enjoy this article? Share your thoughts, questions, or experiences in the comments below — your insights help other readers too.
Join the discussion ↓