Incident Response for Healthcare
When (not if) a security incident occurs, your response determines whether it becomes a manageable event or a catastrophic breach. HIPAA mandates specific breach notification requirements, but effective incident response goes far beyond compliance—it protects patients, preserves evidence, and minimizes business impact.Learning Objectives:
Hands-On Labs: Tabletop exercise + Incident simulation
Prerequisites: HIPAA Fundamentals, Audit Logging
- Build a comprehensive incident response program
- Detect and classify security incidents
- Execute containment and eradication procedures
- Meet HIPAA breach notification requirements
- Conduct forensic investigations
- Perform effective post-incident analysis
Hands-On Labs: Tabletop exercise + Incident simulation
Prerequisites: HIPAA Fundamentals, Audit Logging
HIPAA Breach Notification Requirements
Copy
┌─────────────────────────────────────────────────────────────────────────────┐
│ HIPAA BREACH NOTIFICATION RULE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 45 CFR §§ 164.400-414 - BREACH NOTIFICATION REQUIREMENTS │
│ ──────────────────────────────────────────────────────── │
│ │
│ WHAT IS A BREACH? │
│ ───────────────── │
│ "Acquisition, access, use, or disclosure of PHI in a manner not │
│ permitted under the Privacy Rule which compromises the security │
│ or privacy of the PHI." │
│ │
│ NOTIFICATION TIMELINES: │
│ ─────────────────────── │
│ │
│ ┌──────────────────────┬───────────────────────────────────────────────┐ │
│ │ Affected Parties │ Timeline │ │
│ ├──────────────────────┼───────────────────────────────────────────────┤ │
│ │ Individuals │ Within 60 days of discovery │ │
│ │ HHS (≥500 people) │ Within 60 days of discovery │ │
│ │ HHS (<500 people) │ Within 60 days of calendar year end │ │
│ │ Media (≥500 in state)│ Within 60 days of discovery │ │
│ │ Business Associate │ Notify Covered Entity without unreasonable │ │
│ │ │ delay, no later than 60 days │ │
│ └──────────────────────┴───────────────────────────────────────────────┘ │
│ │
│ EXCEPTION: Encrypted Data │
│ ───────────────────────── │
│ If data was encrypted per NIST standards and the key was not │
│ compromised, it is NOT considered a breach (safe harbor). │
│ │
│ PENALTY TIERS (per violation category per year): │
│ ───────────────────────────────────────────────── │
│ • Tier 1 (Unknowing): $100 - $50,000 (max $1.5M) │
│ • Tier 2 (Reasonable Cause): $1,000 - $50,000 (max $1.5M) │
│ • Tier 3 (Willful Neglect - Corrected): $10,000 - $50,000 (max $1.5M) │
│ • Tier 4 (Willful Neglect - Not Corrected): $50,000 (max $1.5M) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Incident Response Program Structure
The NIST Incident Response Lifecycle
Copy
┌─────────────────────────────────────────────────────────────────────────────┐
│ INCIDENT RESPONSE LIFECYCLE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 1. PREPARATION │ │
│ │ • Policies & procedures • Training │ │
│ │ • Incident response team • Tools & resources │ │
│ │ • Communication plans • Tabletop exercises │ │
│ └───────────────────────────────────┬─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 2. DETECTION & ANALYSIS │ │
│ │ • Monitoring & alerting • Initial triage │ │
│ │ • Incident classification • Scope determination │ │
│ │ • Evidence preservation • Escalation │ │
│ └───────────────────────────────────┬─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 3. CONTAINMENT, ERADICATION, RECOVERY │ │
│ │ • Short-term containment • Evidence collection │ │
│ │ • System isolation • Malware removal │ │
│ │ • Patch vulnerabilities • System restoration │ │
│ │ • Verify clean state • Return to operations │ │
│ └───────────────────────────────────┬─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 4. POST-INCIDENT ACTIVITY │ │
│ │ • Breach determination • Breach notification │ │
│ │ • Lessons learned • Control improvements │ │
│ │ • Documentation update • Metrics & reporting │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Phase 1: Preparation
Incident Response Team Structure
Copy
from dataclasses import dataclass, field
from typing import List, Dict, Optional
from enum import Enum
from datetime import datetime
class IRTeamRole(Enum):
"""Incident Response Team Roles"""
IR_LEAD = "ir_lead"
SECURITY_ANALYST = "security_analyst"
FORENSICS_SPECIALIST = "forensics_specialist"
LEGAL_COUNSEL = "legal_counsel"
PRIVACY_OFFICER = "privacy_officer"
COMMUNICATIONS = "communications"
IT_OPERATIONS = "it_operations"
EXECUTIVE_SPONSOR = "executive_sponsor"
CLINICAL_LIAISON = "clinical_liaison"
HR_REPRESENTATIVE = "hr_representative"
@dataclass
class IRTeamMember:
"""Incident Response Team Member"""
name: str
role: IRTeamRole
primary_phone: str
secondary_phone: str
email: str
backup_person: Optional[str] = None
availability: str = "24/7" # or specific hours
@dataclass
class IncidentResponseTeam:
"""
Incident Response Team Configuration
HIPAA requires designated security incident procedures (§164.308(a)(6))
"""
team_name: str = "Healthcare Security Incident Response Team (HSIRT)"
members: List[IRTeamMember] = field(default_factory=list)
# Escalation thresholds
escalation_levels: Dict[str, List[IRTeamRole]] = field(default_factory=lambda: {
"low": [IRTeamRole.SECURITY_ANALYST],
"medium": [IRTeamRole.SECURITY_ANALYST, IRTeamRole.IR_LEAD],
"high": [
IRTeamRole.IR_LEAD,
IRTeamRole.PRIVACY_OFFICER,
IRTeamRole.LEGAL_COUNSEL
],
"critical": [
IRTeamRole.IR_LEAD,
IRTeamRole.PRIVACY_OFFICER,
IRTeamRole.LEGAL_COUNSEL,
IRTeamRole.EXECUTIVE_SPONSOR,
IRTeamRole.COMMUNICATIONS,
],
})
# Communication channels
primary_channel: str = "Secure Slack #incident-response"
bridge_line: str = "+1-XXX-XXX-XXXX"
war_room_location: str = "Conference Room B, Building 1"
def get_on_call(self, role: IRTeamRole) -> Optional[IRTeamMember]:
"""Get current on-call person for role"""
for member in self.members:
if member.role == role:
return member
return None
def activate_team(self, severity: str) -> List[IRTeamMember]:
"""Activate team members based on severity"""
roles_needed = self.escalation_levels.get(severity, [])
return [
member for member in self.members
if member.role in roles_needed
]
# Example team configuration
ir_team = IncidentResponseTeam(
members=[
IRTeamMember(
name="Sarah Chen",
role=IRTeamRole.IR_LEAD,
primary_phone="+1-555-0100",
secondary_phone="+1-555-0101",
email="[email protected]",
backup_person="Mike Rodriguez",
),
IRTeamMember(
name="James Williams",
role=IRTeamRole.SECURITY_ANALYST,
primary_phone="+1-555-0102",
secondary_phone="+1-555-0103",
email="[email protected]",
),
IRTeamMember(
name="Dr. Emily Foster",
role=IRTeamRole.PRIVACY_OFFICER,
primary_phone="+1-555-0104",
secondary_phone="+1-555-0105",
email="[email protected]",
),
IRTeamMember(
name="Robert Kim",
role=IRTeamRole.LEGAL_COUNSEL,
primary_phone="+1-555-0106",
secondary_phone="+1-555-0107",
email="[email protected]",
),
]
)
Incident Classification
Copy
from enum import Enum
from dataclasses import dataclass
from typing import List, Optional
class IncidentCategory(Enum):
"""Categories of security incidents"""
MALWARE = "malware"
RANSOMWARE = "ransomware"
UNAUTHORIZED_ACCESS = "unauthorized_access"
DATA_EXFILTRATION = "data_exfiltration"
INSIDER_THREAT = "insider_threat"
PHISHING = "phishing"
DENIAL_OF_SERVICE = "denial_of_service"
LOST_DEVICE = "lost_device"
IMPROPER_DISCLOSURE = "improper_disclosure"
SYSTEM_COMPROMISE = "system_compromise"
PHYSICAL_BREACH = "physical_breach"
VENDOR_BREACH = "vendor_breach"
class IncidentSeverity(Enum):
"""Severity levels for incidents"""
CRITICAL = "critical" # Immediate threat to patient safety or massive PHI exposure
HIGH = "high" # Significant PHI exposure or system compromise
MEDIUM = "medium" # Limited PHI exposure or potential for escalation
LOW = "low" # Minor incident, no PHI exposure confirmed
@dataclass
class IncidentClassification:
"""
Incident classification framework
Used to determine response priority and resource allocation
"""
# Classification criteria
phi_involved: bool = False
phi_volume: str = "none" # none, single, limited (<500), large (≥500)
patient_safety_impact: bool = False
systems_affected: List[str] = None
ongoing_threat: bool = False
public_exposure: bool = False
@property
def severity(self) -> IncidentSeverity:
"""Calculate severity based on criteria"""
# Critical conditions
if self.patient_safety_impact:
return IncidentSeverity.CRITICAL
if self.phi_volume == "large" and self.public_exposure:
return IncidentSeverity.CRITICAL
if self.ongoing_threat and self.phi_involved:
return IncidentSeverity.CRITICAL
# High conditions
if self.phi_volume == "large":
return IncidentSeverity.HIGH
if self.public_exposure and self.phi_involved:
return IncidentSeverity.HIGH
if len(self.systems_affected or []) > 5:
return IncidentSeverity.HIGH
# Medium conditions
if self.phi_volume in ["single", "limited"]:
return IncidentSeverity.MEDIUM
if self.phi_involved:
return IncidentSeverity.MEDIUM
return IncidentSeverity.LOW
@property
def response_sla(self) -> dict:
"""Get response SLA based on severity"""
slas = {
IncidentSeverity.CRITICAL: {
"initial_response": "15 minutes",
"containment": "1 hour",
"executive_notification": "30 minutes",
"status_updates": "every 30 minutes",
},
IncidentSeverity.HIGH: {
"initial_response": "30 minutes",
"containment": "4 hours",
"executive_notification": "2 hours",
"status_updates": "every 2 hours",
},
IncidentSeverity.MEDIUM: {
"initial_response": "2 hours",
"containment": "24 hours",
"executive_notification": "next business day",
"status_updates": "daily",
},
IncidentSeverity.LOW: {
"initial_response": "next business day",
"containment": "1 week",
"executive_notification": "as needed",
"status_updates": "weekly",
},
}
return slas[self.severity]
Phase 2: Detection & Analysis
Incident Detection Sources
Copy
from dataclasses import dataclass, field
from datetime import datetime
from typing import List, Optional, Dict, Any
from enum import Enum
import uuid
class DetectionSource(Enum):
"""Sources of incident detection"""
SIEM = "siem"
EDR = "edr"
IDS_IPS = "ids_ips"
ANTIVIRUS = "antivirus"
USER_REPORT = "user_report"
AUDIT_LOG = "audit_log"
THREAT_INTEL = "threat_intel"
VENDOR_NOTIFICATION = "vendor_notification"
LAW_ENFORCEMENT = "law_enforcement"
MEDIA = "media"
DARK_WEB_MONITORING = "dark_web_monitoring"
VULNERABILITY_SCAN = "vulnerability_scan"
@dataclass
class SecurityAlert:
"""Initial security alert triggering investigation"""
alert_id: str = field(default_factory=lambda: str(uuid.uuid4()))
# Source information
source: DetectionSource = DetectionSource.SIEM
source_system: str = ""
source_rule: str = ""
# Alert details
title: str = ""
description: str = ""
raw_data: Dict[str, Any] = field(default_factory=dict)
# Timing
detected_at: datetime = field(default_factory=datetime.utcnow)
event_time: Optional[datetime] = None
# Affected systems
affected_hosts: List[str] = field(default_factory=list)
affected_users: List[str] = field(default_factory=list)
affected_data: List[str] = field(default_factory=list)
# Initial assessment
initial_severity: str = "unknown"
phi_suspected: bool = False
# Status
status: str = "new" # new, investigating, escalated, closed
assigned_to: Optional[str] = None
@dataclass
class Incident:
"""
Security incident record
Created when alert is confirmed as genuine incident
"""
incident_id: str = field(default_factory=lambda: f"INC-{datetime.utcnow().strftime('%Y%m%d')}-{str(uuid.uuid4())[:8].upper()}")
# Classification
category: IncidentCategory = IncidentCategory.UNAUTHORIZED_ACCESS
classification: Optional[IncidentClassification] = None
# Timeline
detected_at: datetime = field(default_factory=datetime.utcnow)
reported_at: Optional[datetime] = None
contained_at: Optional[datetime] = None
eradicated_at: Optional[datetime] = None
recovered_at: Optional[datetime] = None
closed_at: Optional[datetime] = None
# Source
triggered_by_alerts: List[str] = field(default_factory=list)
detection_source: DetectionSource = DetectionSource.SIEM
# Description
title: str = ""
executive_summary: str = ""
technical_details: str = ""
# Impact
affected_systems: List[str] = field(default_factory=list)
affected_users: List[str] = field(default_factory=list)
affected_patients: List[str] = field(default_factory=list) # For breach assessment
phi_types_exposed: List[str] = field(default_factory=list)
# Response
status: str = "open"
current_phase: str = "detection"
assigned_team: List[str] = field(default_factory=list)
ir_lead: Optional[str] = None
# Breach assessment
is_breach: Optional[bool] = None
breach_determination_date: Optional[datetime] = None
breach_notification_required: bool = False
notification_deadline: Optional[datetime] = None
# Evidence
evidence_collected: List[str] = field(default_factory=list)
forensic_images: List[str] = field(default_factory=list)
# Actions taken
actions: List[Dict] = field(default_factory=list)
def add_action(self, action_type: str, description: str, performed_by: str):
"""Log an action taken during incident response"""
self.actions.append({
"timestamp": datetime.utcnow().isoformat(),
"action_type": action_type,
"description": description,
"performed_by": performed_by,
})
def calculate_notification_deadline(self):
"""Calculate breach notification deadline (60 days from discovery)"""
from datetime import timedelta
if self.breach_notification_required:
self.notification_deadline = self.detected_at + timedelta(days=60)
class IncidentManager:
"""Incident lifecycle management"""
def __init__(self):
self.incidents: Dict[str, Incident] = {}
self.alerts: Dict[str, SecurityAlert] = {}
def create_incident_from_alert(
self,
alert: SecurityAlert,
classification: IncidentClassification,
title: str,
created_by: str,
) -> Incident:
"""Create incident from confirmed alert"""
incident = Incident(
category=self._infer_category(alert),
classification=classification,
detected_at=alert.detected_at,
reported_at=datetime.utcnow(),
triggered_by_alerts=[alert.alert_id],
detection_source=alert.source,
title=title,
affected_systems=alert.affected_hosts,
affected_users=alert.affected_users,
)
incident.add_action(
"incident_created",
f"Incident created from alert {alert.alert_id}",
created_by,
)
# Update alert status
alert.status = "escalated"
self.incidents[incident.incident_id] = incident
# Trigger notifications based on severity
self._trigger_notifications(incident)
return incident
def update_incident_phase(
self,
incident_id: str,
new_phase: str,
updated_by: str,
notes: str = "",
):
"""Update incident phase"""
incident = self.incidents.get(incident_id)
if not incident:
return
old_phase = incident.current_phase
incident.current_phase = new_phase
# Set phase timestamps
if new_phase == "containment":
incident.contained_at = datetime.utcnow()
elif new_phase == "eradication":
incident.eradicated_at = datetime.utcnow()
elif new_phase == "recovery":
incident.recovered_at = datetime.utcnow()
elif new_phase == "closed":
incident.closed_at = datetime.utcnow()
incident.status = "closed"
incident.add_action(
"phase_change",
f"Phase changed from {old_phase} to {new_phase}. {notes}",
updated_by,
)
def _infer_category(self, alert: SecurityAlert) -> IncidentCategory:
"""Infer incident category from alert"""
title_lower = alert.title.lower()
if "ransomware" in title_lower:
return IncidentCategory.RANSOMWARE
if "malware" in title_lower:
return IncidentCategory.MALWARE
if "phishing" in title_lower:
return IncidentCategory.PHISHING
if "exfil" in title_lower:
return IncidentCategory.DATA_EXFILTRATION
if "insider" in title_lower:
return IncidentCategory.INSIDER_THREAT
return IncidentCategory.UNAUTHORIZED_ACCESS
def _trigger_notifications(self, incident: Incident):
"""Trigger notifications based on severity"""
severity = incident.classification.severity if incident.classification else IncidentSeverity.MEDIUM
notification = {
"incident_id": incident.incident_id,
"severity": severity.value,
"title": incident.title,
"phi_involved": incident.classification.phi_involved if incident.classification else False,
"timestamp": datetime.utcnow().isoformat(),
}
# In production: Send to appropriate channels
print(f"NOTIFICATION: {notification}")
Phase 3: Containment, Eradication, Recovery
Containment Playbooks
Copy
from dataclasses import dataclass
from typing import List, Callable
from enum import Enum
@dataclass
class PlaybookStep:
"""Single step in incident response playbook"""
step_number: int
title: str
description: str
responsible_role: str
estimated_time: str
verification: str # How to verify step completion
automated: bool = False
automation_script: str = ""
@dataclass
class IncidentPlaybook:
"""
Incident response playbook
Pre-defined response procedures for common incident types
"""
playbook_id: str
name: str
incident_category: IncidentCategory
description: str
# Steps
containment_steps: List[PlaybookStep]
eradication_steps: List[PlaybookStep]
recovery_steps: List[PlaybookStep]
# Requirements
required_tools: List[str]
required_access: List[str]
# Approvals
requires_executive_approval: bool = False
requires_legal_approval: bool = False
# Ransomware Playbook
RANSOMWARE_PLAYBOOK = IncidentPlaybook(
playbook_id="PB-001",
name="Ransomware Response",
incident_category=IncidentCategory.RANSOMWARE,
description="Response procedures for ransomware infections",
containment_steps=[
PlaybookStep(
step_number=1,
title="Isolate Infected Systems",
description="""
IMMEDIATELY disconnect infected systems from network:
1. Disable network ports at switch level
2. Disable Wi-Fi adapters
3. Do NOT power off systems (preserves memory evidence)
4. Block IP addresses at firewall
""",
responsible_role="IT Operations",
estimated_time="15 minutes",
verification="Ping test to isolated systems fails",
),
PlaybookStep(
step_number=2,
title="Block Lateral Movement",
description="""
Prevent spread to other systems:
1. Block SMB (ports 445, 139) internally
2. Disable RDP between systems
3. Block PowerShell remoting
4. Isolate network segments if needed
""",
responsible_role="Network Security",
estimated_time="30 minutes",
verification="Network scans show blocked ports",
),
PlaybookStep(
step_number=3,
title="Preserve Evidence",
description="""
Capture volatile data before any changes:
1. Memory dumps of infected systems
2. Network traffic captures
3. Ransom notes and encrypted file samples
4. Screenshot of ransom demands
""",
responsible_role="Forensics",
estimated_time="1-2 hours",
verification="Evidence chain of custody documented",
),
PlaybookStep(
step_number=4,
title="Assess Scope",
description="""
Determine extent of infection:
1. Query EDR for IoCs across all endpoints
2. Check file shares for encrypted files
3. Review backup system status
4. Identify PHI systems affected
""",
responsible_role="Security Analyst",
estimated_time="2-4 hours",
verification="Scope document completed",
),
],
eradication_steps=[
PlaybookStep(
step_number=1,
title="Identify Ransomware Variant",
description="""
Determine specific ransomware family:
1. Check ID Ransomware (id-ransomware.malwarehunterteam.com)
2. Analyze ransom note format
3. Check for decryptor availability
4. Report to FBI IC3 if in US
""",
responsible_role="Security Analyst",
estimated_time="1 hour",
verification="Ransomware variant identified",
),
PlaybookStep(
step_number=2,
title="Clean Infected Systems",
description="""
Remove ransomware:
1. For non-critical: Wipe and reimage
2. For critical: Full forensic cleaning if possible
3. Remove persistence mechanisms
4. Reset all credentials accessed from infected systems
""",
responsible_role="IT Operations",
estimated_time="4-8 hours per system",
verification="EDR confirms no IoCs present",
),
PlaybookStep(
step_number=3,
title="Close Entry Vector",
description="""
Eliminate how attacker got in:
1. Patch exploited vulnerabilities
2. Block phishing email IOCs
3. Update email filters
4. Strengthen authentication (add MFA)
""",
responsible_role="Security Engineering",
estimated_time="2-4 hours",
verification="Entry vector documented and closed",
),
],
recovery_steps=[
PlaybookStep(
step_number=1,
title="Validate Backups",
description="""
Ensure backups are clean and complete:
1. Scan backup media for ransomware
2. Verify backup integrity
3. Test restore procedures
4. Determine recovery point (last clean backup)
""",
responsible_role="IT Operations",
estimated_time="2-4 hours",
verification="Backup validation report",
),
PlaybookStep(
step_number=2,
title="Restore Systems",
description="""
Begin phased restoration:
1. Prioritize critical clinical systems
2. Restore from verified clean backups
3. Validate application functionality
4. Monitor for reinfection
""",
responsible_role="IT Operations",
estimated_time="Variable (hours to days)",
verification="Systems operational and monitored",
),
PlaybookStep(
step_number=3,
title="Return to Normal Operations",
description="""
Safely resume operations:
1. Reconnect systems to network gradually
2. Enhanced monitoring for 30 days
3. User password resets
4. Document incident lessons learned
""",
responsible_role="IT Operations",
estimated_time="1-2 weeks",
verification="All systems operational, monitoring in place",
),
],
required_tools=[
"EDR console access",
"Network monitoring tools",
"Memory forensics toolkit",
"Backup/restore system access",
"Firewall management access",
],
required_access=[
"Domain admin credentials (break-glass)",
"Network infrastructure access",
"Backup system access",
"Cloud console access",
],
requires_executive_approval=True, # For ransom payment decisions
requires_legal_approval=True, # For breach notification
)
# Data Exfiltration Playbook
DATA_EXFILTRATION_PLAYBOOK = IncidentPlaybook(
playbook_id="PB-002",
name="Data Exfiltration Response",
incident_category=IncidentCategory.DATA_EXFILTRATION,
description="Response procedures for confirmed or suspected data theft",
containment_steps=[
PlaybookStep(
step_number=1,
title="Block Active Exfiltration",
description="""
Stop ongoing data theft:
1. Block destination IP addresses at firewall
2. Disable suspect user accounts
3. Block USB/external media
4. Terminate suspicious processes
""",
responsible_role="Security Operations",
estimated_time="15 minutes",
verification="No active outbound connections to destinations",
),
PlaybookStep(
step_number=2,
title="Preserve Evidence",
description="""
Capture all relevant evidence:
1. Network traffic captures (PCAP)
2. Proxy/firewall logs
3. DLP alerts
4. User activity logs
5. Email logs if email was exfil method
""",
responsible_role="Forensics",
estimated_time="2 hours",
verification="Evidence preserved and documented",
),
PlaybookStep(
step_number=3,
title="Assess Data Involved",
description="""
Determine what data was exfiltrated:
1. Review DLP logs for data classification
2. Analyze file access logs
3. Identify affected databases/file shares
4. Determine if PHI involved
5. Count affected patient records if PHI
""",
responsible_role="Security Analyst",
estimated_time="4-8 hours",
verification="Data assessment report completed",
),
],
eradication_steps=[
PlaybookStep(
step_number=1,
title="Close Access Vector",
description="""
Eliminate attacker access:
1. Revoke all access for compromised accounts
2. Reset credentials
3. Revoke API keys/tokens
4. Review and revoke third-party access
""",
responsible_role="IT Security",
estimated_time="2-4 hours",
verification="All compromised access revoked",
),
PlaybookStep(
step_number=2,
title="Sweep for Persistence",
description="""
Check for ongoing access:
1. Hunt for backdoors
2. Review authorized SSH keys
3. Check for rogue admin accounts
4. Verify cloud IAM permissions
""",
responsible_role="Security Operations",
estimated_time="4-8 hours",
verification="No unauthorized access mechanisms found",
),
],
recovery_steps=[
PlaybookStep(
step_number=1,
title="Implement Enhanced Monitoring",
description="""
Increase detection capability:
1. Enhance DLP rules
2. Add network monitoring for affected data types
3. Implement user behavior analytics
4. Increase log retention
""",
responsible_role="Security Engineering",
estimated_time="1-2 days",
verification="Enhanced monitoring operational",
),
PlaybookStep(
step_number=2,
title="Breach Notification (if required)",
description="""
Execute notification requirements:
1. Complete breach risk assessment
2. Prepare notification letters
3. Notify affected individuals within 60 days
4. Notify HHS if ≥500 individuals
5. Notify media if ≥500 in a state
""",
responsible_role="Privacy Officer",
estimated_time="Variable",
verification="All notifications sent and documented",
),
],
required_tools=[
"DLP console",
"Network monitoring (PCAP capability)",
"SIEM/log analysis",
"Identity management system",
],
required_access=[
"DLP admin",
"Network infrastructure",
"Identity management admin",
"Log management system",
],
requires_executive_approval=False,
requires_legal_approval=True, # For breach notification
)
Phase 4: Breach Determination & Notification
HIPAA Breach Risk Assessment
Copy
from dataclasses import dataclass
from typing import List, Optional
from enum import Enum
from datetime import datetime
class RiskFactor(Enum):
"""HIPAA breach risk assessment factors"""
NATURE_AND_EXTENT = "nature_and_extent"
UNAUTHORIZED_PERSON = "unauthorized_person"
PHI_ACQUIRED_VIEWED = "phi_acquired_viewed"
RISK_MITIGATION = "risk_mitigation"
@dataclass
class BreachRiskAssessment:
"""
HIPAA Breach Risk Assessment
Per 45 CFR 164.402, breach assessment must consider:
1. Nature and extent of PHI involved
2. Unauthorized person who used PHI or to whom it was disclosed
3. Whether PHI was actually acquired or viewed
4. Extent to which risk has been mitigated
"""
incident_id: str
assessment_date: datetime = None
assessor: str = ""
# Factor 1: Nature and extent of PHI
phi_types: List[str] = None # SSN, diagnosis, treatment, financial
phi_sensitivity: str = "" # low, medium, high (mental health, HIV, substance abuse = high)
number_of_individuals: int = 0
# Factor 2: Unauthorized person
unauthorized_person_identified: bool = False
unauthorized_person_type: str = "" # employee, external, unknown
likelihood_of_reidentification: str = "" # low, medium, high
# Factor 3: PHI acquired or viewed
phi_actually_acquired: bool = False
phi_actually_viewed: bool = False
evidence_of_access: str = ""
# Factor 4: Mitigation
mitigation_steps: List[str] = None
mitigation_effectiveness: str = "" # none, partial, full
# Encryption status (safe harbor)
data_was_encrypted: bool = False
encryption_meets_nist: bool = False
encryption_key_compromised: bool = False
# Assessment outcome
breach_determination: Optional[bool] = None
determination_rationale: str = ""
@property
def qualifies_for_safe_harbor(self) -> bool:
"""Check if encryption safe harbor applies"""
return (
self.data_was_encrypted and
self.encryption_meets_nist and
not self.encryption_key_compromised
)
def calculate_risk_score(self) -> dict:
"""Calculate risk score for each factor"""
scores = {
"nature_extent": 0,
"unauthorized_person": 0,
"phi_accessed": 0,
"mitigation": 0,
}
# Factor 1: Nature and extent
if "ssn" in (self.phi_types or []):
scores["nature_extent"] += 3
if "financial" in (self.phi_types or []):
scores["nature_extent"] += 3
if self.phi_sensitivity == "high":
scores["nature_extent"] += 3
if self.number_of_individuals > 500:
scores["nature_extent"] += 2
elif self.number_of_individuals > 100:
scores["nature_extent"] += 1
# Factor 2: Unauthorized person
if self.unauthorized_person_type == "external":
scores["unauthorized_person"] += 3
elif self.unauthorized_person_type == "employee":
scores["unauthorized_person"] += 2
if self.likelihood_of_reidentification == "high":
scores["unauthorized_person"] += 2
# Factor 3: PHI accessed
if self.phi_actually_acquired:
scores["phi_accessed"] += 3
elif self.phi_actually_viewed:
scores["phi_accessed"] += 2
elif not self.phi_actually_acquired and not self.phi_actually_viewed:
scores["phi_accessed"] += 0 # Reduces overall risk
# Factor 4: Mitigation (reduces risk)
if self.mitigation_effectiveness == "full":
scores["mitigation"] -= 3
elif self.mitigation_effectiveness == "partial":
scores["mitigation"] -= 1
return scores
def determine_breach(self) -> tuple[bool, str]:
"""
Determine if incident constitutes a breach
Returns:
Tuple of (is_breach: bool, rationale: str)
"""
# Check safe harbor first
if self.qualifies_for_safe_harbor:
return False, "Encryption safe harbor applies - data was encrypted per NIST standards and key was not compromised"
# Calculate risk
scores = self.calculate_risk_score()
total_score = sum(scores.values())
# Low probability of compromise = not a breach
if total_score <= 2:
return False, f"Low probability of compromise (risk score: {total_score}). No notification required."
# High probability = breach
if total_score >= 6:
return True, f"High probability of compromise (risk score: {total_score}). Breach notification required."
# Medium - document carefully
return True, f"Moderate probability of compromise (risk score: {total_score}). Recommend treating as breach."
@dataclass
class BreachNotification:
"""
HIPAA Breach Notification Record
"""
incident_id: str
# What
breach_description: str = ""
phi_types_involved: List[str] = None
# When
discovery_date: datetime = None
notification_deadline: datetime = None # 60 days from discovery
# Who
number_affected: int = 0
# Notifications sent
individual_notifications: List[dict] = None
hhs_notification_date: Optional[datetime] = None
media_notification_date: Optional[datetime] = None
# Required content (per 45 CFR 164.404)
notification_content: dict = None
def generate_notification_content(self) -> dict:
"""Generate required notification content"""
return {
"brief_description": self.breach_description,
"phi_types": self.phi_types_involved,
"steps_for_individuals": [
"Monitor credit reports and statements",
"Consider placing fraud alerts",
"Report suspicious activity",
],
"what_we_are_doing": [
"Investigating the incident",
"Implementing additional safeguards",
"Offering credit monitoring services",
],
"contact_information": {
"phone": "1-800-XXX-XXXX",
"email": "[email protected]",
"mail": "Privacy Officer, Organization Address",
},
}
def generate_individual_letter(self, patient_name: str, patient_address: str) -> str:
"""Generate breach notification letter for individual"""
content = self.generate_notification_content()
letter = f"""
[ORGANIZATION LETTERHEAD]
{datetime.now().strftime("%B %d, %Y")}
{patient_name}
{patient_address}
RE: Notice of Data Breach
Dear {patient_name},
We are writing to inform you of an incident that may have affected the privacy of your
protected health information (PHI).
WHAT HAPPENED
{content['brief_description']}
WHAT INFORMATION WAS INVOLVED
The following types of information may have been affected:
{', '.join(content['phi_types'])}
WHAT WE ARE DOING
{chr(10).join('• ' + step for step in content['what_we_are_doing'])}
WHAT YOU CAN DO
We recommend you take the following steps:
{chr(10).join('• ' + step for step in content['steps_for_individuals'])}
FOR MORE INFORMATION
If you have questions, please contact us:
Phone: {content['contact_information']['phone']}
Email: {content['contact_information']['email']}
Mail: {content['contact_information']['mail']}
We sincerely apologize for any inconvenience this may cause.
Sincerely,
[Privacy Officer Name]
Privacy Officer
"""
return letter
Post-Incident Analysis
Lessons Learned Template
Copy
from dataclasses import dataclass, field
from datetime import datetime
from typing import List, Dict
@dataclass
class LessonsLearned:
"""
Post-incident lessons learned documentation
HIPAA requires evaluation and updates based on incidents (§164.308(a)(8))
"""
incident_id: str
review_date: datetime = field(default_factory=datetime.utcnow)
participants: List[str] = field(default_factory=list)
# Timeline reconstruction
timeline: List[Dict] = field(default_factory=list)
# What worked well
what_worked: List[str] = field(default_factory=list)
# What didn't work
what_didnt_work: List[str] = field(default_factory=list)
# Root cause analysis
root_causes: List[str] = field(default_factory=list)
contributing_factors: List[str] = field(default_factory=list)
# Improvement actions
improvement_actions: List[Dict] = field(default_factory=list)
# Metrics
time_to_detect: str = ""
time_to_contain: str = ""
time_to_eradicate: str = ""
time_to_recover: str = ""
total_downtime: str = ""
estimated_cost: float = 0.0
def add_improvement_action(
self,
action: str,
owner: str,
due_date: str,
priority: str = "medium",
):
"""Add improvement action with tracking"""
self.improvement_actions.append({
"action": action,
"owner": owner,
"due_date": due_date,
"priority": priority,
"status": "open",
"created_at": datetime.utcnow().isoformat(),
})
def generate_report(self) -> str:
"""Generate lessons learned report"""
report = f"""
# Incident Post-Mortem Report
## Incident: {self.incident_id}
**Review Date:** {self.review_date.strftime("%Y-%m-%d")}
**Participants:** {", ".join(self.participants)}
---
## Timeline
| Time | Event |
|------|-------|
"""
for event in self.timeline:
report += f"| {event['time']} | {event['event']} |\n"
report += f"""
---
## Response Metrics
- **Time to Detect:** {self.time_to_detect}
- **Time to Contain:** {self.time_to_contain}
- **Time to Eradicate:** {self.time_to_eradicate}
- **Time to Recover:** {self.time_to_recover}
- **Total Downtime:** {self.total_downtime}
- **Estimated Cost:** ${self.estimated_cost:,.2f}
---
## What Worked Well
"""
for item in self.what_worked:
report += f"- {item}\n"
report += "\n## What Didn't Work Well\n"
for item in self.what_didnt_work:
report += f"- {item}\n"
report += "\n## Root Causes\n"
for cause in self.root_causes:
report += f"- {cause}\n"
report += "\n## Improvement Actions\n"
report += "| Action | Owner | Due Date | Priority | Status |\n"
report += "|--------|-------|----------|----------|--------|\n"
for action in self.improvement_actions:
report += f"| {action['action']} | {action['owner']} | {action['due_date']} | {action['priority']} | {action['status']} |\n"
return report
# Example lessons learned
lessons = LessonsLearned(
incident_id="INC-20240115-A1B2C3D4",
participants=["IR Lead", "Security Analyst", "Privacy Officer", "IT Director"],
)
lessons.timeline = [
{"time": "2024-01-15 14:30", "event": "SIEM alert triggered for unusual file access"},
{"time": "2024-01-15 14:45", "event": "Security analyst began investigation"},
{"time": "2024-01-15 15:00", "event": "Incident confirmed, IR Lead notified"},
{"time": "2024-01-15 15:30", "event": "Affected account disabled"},
{"time": "2024-01-15 16:00", "event": "Scope assessment completed"},
{"time": "2024-01-16 10:00", "event": "Forensic analysis completed"},
{"time": "2024-01-17 14:00", "event": "Recovery completed"},
]
lessons.what_worked = [
"SIEM alerting detected anomaly within 30 minutes",
"IR team responded within SLA",
"Audit logs were complete and available",
"Backup restoration was successful",
]
lessons.what_didnt_work = [
"Initial triage took too long (45 minutes)",
"Communication with executives was delayed",
"DLP alert was missed the day before",
]
lessons.root_causes = [
"User clicked on phishing link leading to credential theft",
"Lack of MFA on affected system",
]
lessons.add_improvement_action(
action="Implement MFA on all PHI systems",
owner="IT Security",
due_date="2024-02-28",
priority="high",
)
lessons.add_improvement_action(
action="Enhance phishing training program",
owner="HR/Training",
due_date="2024-03-15",
priority="high",
)
Hands-On Lab: Tabletop Exercise
1
Scenario Setup
Your organization is a regional healthcare system with 3 hospitals and 50 clinics.
On Monday at 9:00 AM, your SIEM alerts to unusual activity: a clinical workstation
in the ER is making thousands of rapid database queries to the EHR.
2
Exercise Questions
Walk through the incident response:
- Detection: What additional information do you need? Who do you notify first?
- Classification: Based on initial data, what severity level? What’s your response SLA?
- Containment: What are your first 3 containment actions?
- Investigation: What logs and evidence do you preserve?
- Breach Assessment: 48 hours later, you confirm 15,000 patient records were accessed. Is this a breach? What’s your notification timeline?
3
Document Your Response
Create:
- Initial incident ticket
- Classification worksheet
- Containment checklist
- Breach risk assessment
- Notification timeline
Key Takeaways
Preparation is Everything
Incident response is 90% preparation. Build playbooks, train teams, practice regularly.
60-Day Clock
HIPAA breach notification begins at discovery. Know your timeline.
Document Everything
Every action, decision, and finding must be documented for compliance and legal purposes.
Encryption = Safe Harbor
Properly encrypted data with uncompromised keys is NOT a breach. Encrypt everything.