NEN 7510: Healthcare Information Security in the Netherlands
Implementing NEN 7510 for healthcare organisations. Understanding Dutch healthcare security requirements, integration with ISO 27001, and practical compliance strategies.
NEN 7510: Healthcare Information Security in the Netherlands
NEN 7510 is the Dutch standard for information security in healthcare. For organisations processing health data in the Netherlands, compliance isn't optional—it's a legal requirement under the Wet aanvullende bepalingen verwerking persoonsgegevens in de zorg (Wabvpz).
Understanding NEN 7510
The NEN 7510 Family
NEN 7510 Standards Family:
├── NEN 7510-1:2017
│ └── Information security management systems in healthcare
│ (Based on ISO 27001, healthcare-specific requirements)
│
├── NEN 7510-2:2017
│ └── Controls for healthcare
│ (Based on ISO 27002, healthcare adaptations)
│
├── NEN 7512:2022
│ └── Electronic communication in healthcare
│ (Data exchange, messaging, interoperability)
│
├── NEN 7513:2023
│ └── Logging of patient data access
│ (Audit trails, access monitoring)
│
└── NEN 7516:2021
└── Remote access to patient data
(Telehealth, mobile access, home working)Key Differences from ISO 27001
| Aspect | ISO 27001 | NEN 7510 |
|---|---|---|
| Scope | General information security | Healthcare-specific |
| Patient rights | Not addressed | Central requirement |
| Logging | General requirement | Detailed in NEN 7513 |
| Data exchange | General | Specific in NEN 7512 |
| Risk appetite | Organisation defines | Lower tolerance for health data |
| Legal basis | International standard | Dutch legal requirement |
Legal Context
Dutch Healthcare Law Requirements
# legal-framework.yml
wabvpz_requirements:
- name: Information Security Obligation
article: Article 3
requirement: |
Healthcare providers must take appropriate technical
and organisational measures to secure health data
implementation: NEN 7510 compliance
- name: Electronic Data Exchange
article: Article 4
requirement: |
Electronic exchange of health data must meet
security requirements
implementation: NEN 7512 compliance
- name: Access Logging
article: Article 5
requirement: |
Access to patient records must be logged
implementation: NEN 7513 compliance
- name: Patient Rights
article: Article 6
requirement: |
Patients have right to access logs of who
viewed their data
implementation: Log access portal, NEN 7513
avg_gdpr_intersection:
- Health data as special category (Article 9)
- Data protection by design (Article 25)
- Security of processing (Article 32)
- Data breach notification (Article 33)
- Data Protection Impact Assessment (Article 35)Enforcement and Penalties
Regulatory Oversight:
├── Autoriteit Persoonsgegevens (AP)
│ ├── GDPR/AVG enforcement
│ ├── Data breach investigations
│ └── Fines up to €20M or 4% turnover
│
├── Inspectie Gezondheidszorg en Jeugd (IGJ)
│ ├── Healthcare quality oversight
│ ├── NEN 7510 compliance verification
│ └── Can mandate improvements or close facilities
│
└── NZa (Nederlandse Zorgautoriteit)
├── Healthcare market regulation
└── Can impose administrative measuresNEN 7510 Control Framework
Healthcare-Specific Controls
// nen7510-controls.ts
interface HealthcareControl {
id: string;
isoEquivalent?: string;
requirement: string;
healthcareContext: string;
implementationGuidance: string[];
}
const healthcareSpecificControls: HealthcareControl[] = [
{
id: 'NEN7510-9.4.1',
isoEquivalent: 'ISO27001-A.9.4.1',
requirement: 'Information access restriction',
healthcareContext: `
Healthcare workers must only access patient data
for patients under their direct care. Access must
be role-based and time-limited where applicable.
`,
implementationGuidance: [
'Implement treatment relationship verification',
'Configure role-based access per department',
'Enable break-the-glass for emergencies',
'Log all access with reason codes'
]
},
{
id: 'NEN7510-12.4',
isoEquivalent: 'ISO27001-A.12.4',
requirement: 'Logging and monitoring',
healthcareContext: `
All access to patient data must be logged in
accordance with NEN 7513. Logs must be retained
and patients must be able to request access logs.
`,
implementationGuidance: [
'Log who, what, when, why for all access',
'Retain logs for minimum 5 years',
'Implement patient log access portal',
'Enable anomaly detection on access patterns'
]
},
{
id: 'NEN7510-13.2',
isoEquivalent: 'ISO27001-A.13.2',
requirement: 'Information transfer',
healthcareContext: `
Health data exchange must comply with NEN 7512.
Use approved secure messaging standards and
verify recipient identity before transmission.
`,
implementationGuidance: [
'Use AORTA/LSP for national exchange',
'Implement secure email (ZIVVER/similar)',
'Verify UZI/AGB codes for recipients',
'Encrypt data in transit and at rest'
]
},
{
id: 'NEN7510-18.1',
isoEquivalent: 'ISO27001-A.18.1',
requirement: 'Legal compliance',
healthcareContext: `
Must comply with Wabvpz, WGBO, AVG/GDPR,
and sector-specific regulations. Patient
rights must be actively supported.
`,
implementationGuidance: [
'Map all applicable regulations',
'Implement patient consent management',
'Enable data portability (Article 20 GDPR)',
'Support right to be forgotten where applicable'
]
}
];Treatment Relationship Model
// treatment-relationship.ts
interface TreatmentRelationship {
patientId: string;
healthcareProviderId: string;
providerUZI: string;
organisationAGB: string;
relationshipType: 'direct_care' | 'consultation' | 'emergency' | 'administrative';
validFrom: Date;
validUntil?: Date;
accessScope: AccessScope;
}
interface AccessScope {
dataCategories: DataCategory[];
timeRestriction?: {
startTime: string;
endTime: string;
};
locationRestriction?: string[];
}
type DataCategory =
| 'demographics'
| 'medical_history'
| 'medications'
| 'lab_results'
| 'imaging'
| 'clinical_notes'
| 'mental_health'
| 'hiv_status'
| 'genetic_data';
const checkAccessAuthorisation = async (
userId: string,
patientId: string,
requestedData: DataCategory[]
): Promise<AccessDecision> => {
// Find active treatment relationship
const relationship = await findActiveRelationship(userId, patientId);
if (!relationship) {
// Check for emergency access (break-the-glass)
return {
allowed: false,
reason: 'no_treatment_relationship',
emergencyAccessAvailable: true
};
}
// Verify requested data is within scope
const unauthorisedCategories = requestedData.filter(
cat => !relationship.accessScope.dataCategories.includes(cat)
);
if (unauthorisedCategories.length > 0) {
return {
allowed: false,
reason: 'data_outside_scope',
unauthorisedCategories,
emergencyAccessAvailable: true
};
}
return {
allowed: true,
relationship,
mustLog: true,
logReason: 'treatment_relationship'
};
};NEN 7513: Logging Requirements
Comprehensive Access Logging
// nen7513-logging.ts
interface PatientDataAccessLog {
// Mandatory fields per NEN 7513
logId: string;
timestamp: Date;
// Who accessed
userId: string;
userRole: string;
userUZI?: string;
organisationId: string;
organisationAGB: string;
// What was accessed
patientId: string;
patientBSN?: string;
resourceType: string;
resourceId: string;
dataCategories: DataCategory[];
// How accessed
action: 'create' | 'read' | 'update' | 'delete' | 'print' | 'export';
accessMethod: 'application' | 'api' | 'direct_db' | 'emergency';
// Why accessed
accessReason: AccessReason;
reasonDescription?: string;
// Context
applicationId: string;
applicationVersion: string;
clientIP?: string;
deviceId?: string;
}
type AccessReason =
| 'direct_care'
| 'consultation_request'
| 'emergency'
| 'quality_review'
| 'research_anonymised'
| 'administrative'
| 'patient_request'
| 'legal_requirement';
const logPatientDataAccess = async (
access: Omit<PatientDataAccessLog, 'logId' | 'timestamp'>
): Promise<void> => {
const log: PatientDataAccessLog = {
...access,
logId: generateUUID(),
timestamp: new Date()
};
// Write to immutable log storage
await writeToImmutableLog(log);
// Real-time anomaly detection
await checkForAnomalies(log);
// Update patient access dashboard
await updatePatientAccessDashboard(log.patientId, log);
};
const writeToImmutableLog = async (log: PatientDataAccessLog): Promise<void> => {
// Write to append-only storage with cryptographic integrity
const logEntry = {
...log,
previousHash: await getLastLogHash(),
hash: await calculateLogHash(log)
};
await appendOnlyStorage.write(logEntry);
// Replicate to secure backup
await replicateToBackup(logEntry);
};Patient Access Portal
// patient-access-portal.ts
interface PatientAccessView {
patientId: string;
accessHistory: AccessHistoryEntry[];
totalAccesses: number;
uniqueProviders: number;
lastAccess: Date;
}
interface AccessHistoryEntry {
date: Date;
providerName: string;
organisationName: string;
action: string;
dataAccessed: string;
reason: string;
}
const getPatientAccessHistory = async (
patientId: string,
authenticatedPatientBSN: string,
options: { from?: Date; to?: Date; limit?: number }
): Promise<PatientAccessView> => {
// Verify patient identity
if (!await verifyPatientIdentity(patientId, authenticatedPatientBSN)) {
throw new UnauthorisedError('Patient identity verification failed');
}
// Retrieve access logs
const logs = await accessLogStore.query({
patientId,
from: options.from || new Date(Date.now() - 365 * 24 * 60 * 60 * 1000),
to: options.to || new Date(),
limit: options.limit || 100
});
// Transform to patient-friendly view
const accessHistory = await Promise.all(
logs.map(async log => ({
date: log.timestamp,
providerName: await resolveProviderName(log.userId),
organisationName: await resolveOrganisationName(log.organisationId),
action: translateAction(log.action),
dataAccessed: translateDataCategories(log.dataCategories),
reason: translateReason(log.accessReason)
}))
);
return {
patientId,
accessHistory,
totalAccesses: logs.length,
uniqueProviders: new Set(logs.map(l => l.userId)).size,
lastAccess: logs[0]?.timestamp || null
};
};NEN 7512: Secure Data Exchange
Healthcare Messaging Standards
# nen7512-exchange-standards.yml
approved_exchange_methods:
national_infrastructure:
- name: LSP (Landelijk Schakelpunt)
use_case: National patient summary exchange
authentication: UZI-pas certificate
protocol: HL7v3
encryption: TLS 1.2+
- name: AORTA
use_case: Medication records, lab results
authentication: UZI certificate
protocol: HL7 FHIR
encryption: TLS 1.3
- name: MedMij
use_case: Patient-controlled data exchange
authentication: DigiD
protocol: FHIR R4
encryption: TLS 1.3
secure_messaging:
- name: ZIVVER
use_case: Secure email, file transfer
authentication: Email + 2FA
encryption: End-to-end
- name: Siilo
use_case: Clinical messaging
authentication: Phone verification
encryption: End-to-end
file_transfer:
- name: VECOZO
use_case: Healthcare declarations
authentication: Certificate
protocol: AS2/AS4
encryption: TLS + message-levelSecure Message Exchange Implementation
// secure-healthcare-exchange.ts
interface SecureMessage {
messageId: string;
sender: HealthcareEntity;
recipient: HealthcareEntity;
patient: PatientReference;
content: EncryptedContent;
metadata: MessageMetadata;
signature: DigitalSignature;
}
interface HealthcareEntity {
type: 'practitioner' | 'organisation';
identifier: string;
uziNumber?: string;
agbCode?: string;
certificateThumbprint: string;
}
const sendSecureHealthcareMessage = async (
message: UnencryptedMessage,
recipientIdentifier: string
): Promise<SendResult> => {
// Step 1: Verify recipient
const recipient = await verifyHealthcareRecipient(recipientIdentifier);
if (!recipient.verified) {
throw new RecipientVerificationError(
`Cannot verify healthcare provider: ${recipientIdentifier}`
);
}
// Step 2: Check patient consent if required
if (message.patient && !await hasPatientConsent(message.patient.id, recipient)) {
throw new ConsentError('Patient consent not recorded for this recipient');
}
// Step 3: Encrypt message content
const encryptedContent = await encryptForRecipient(
message.content,
recipient.publicKey
);
// Step 4: Sign message
const signature = await signMessage({
messageId: message.id,
contentHash: await hashContent(message.content),
timestamp: new Date(),
senderId: getCurrentUserUZI()
});
// Step 5: Send via appropriate channel
const secureMessage: SecureMessage = {
messageId: message.id,
sender: await getCurrentSenderEntity(),
recipient: recipient.entity,
patient: message.patient,
content: encryptedContent,
metadata: {
sent: new Date(),
contentType: message.contentType,
urgency: message.urgency
},
signature
};
// Step 6: Log the exchange
await logHealthcareExchange(secureMessage);
return await transmitSecureMessage(secureMessage);
};
const verifyHealthcareRecipient = async (
identifier: string
): Promise<RecipientVerification> => {
// Check UZI register
const uziVerification = await uziRegister.verify(identifier);
if (uziVerification.valid) {
return {
verified: true,
entity: uziVerification.entity,
publicKey: uziVerification.certificate.publicKey,
verificationMethod: 'UZI'
};
}
// Check AGB register
const agbVerification = await agbRegister.verify(identifier);
if (agbVerification.valid) {
return {
verified: true,
entity: agbVerification.entity,
publicKey: await retrieveOrganisationKey(agbVerification.agbCode),
verificationMethod: 'AGB'
};
}
return { verified: false };
};Implementation Roadmap
Phase 1: Assessment and Planning
# phase1-assessment.yml
activities:
gap_analysis:
- Review current security controls against NEN 7510
- Identify healthcare-specific gaps
- Assess NEN 7513 logging compliance
- Evaluate NEN 7512 exchange mechanisms
duration: 4-6 weeks
risk_assessment:
- Identify healthcare data assets
- Assess threats specific to healthcare
- Evaluate patient safety impacts
- Document risk treatment decisions
duration: 3-4 weeks
scope_definition:
- Define ISMS boundaries
- Identify critical healthcare processes
- Map patient data flows
- Document third-party relationships
duration: 2 weeks
deliverables:
- Gap analysis report
- Healthcare-specific risk register
- ISMS scope document
- Implementation project planPhase 2: Core Implementation
// implementation-checklist.ts
interface ImplementationPhase {
name: string;
controls: ControlImplementation[];
dependencies: string[];
duration: string;
}
const phase2Implementation: ImplementationPhase = {
name: 'Core Security Controls',
duration: '3-4 months',
dependencies: ['phase1_completed'],
controls: [
{
control: 'Access Control',
tasks: [
'Implement role-based access control',
'Configure treatment relationship checks',
'Deploy break-the-glass procedures',
'Enable UZI authentication where required'
],
priority: 'critical'
},
{
control: 'NEN 7513 Logging',
tasks: [
'Deploy centralised logging infrastructure',
'Configure all applications for NEN 7513 compliance',
'Implement log integrity protection',
'Build patient access portal'
],
priority: 'critical'
},
{
control: 'Data Protection',
tasks: [
'Implement encryption at rest (AES-256)',
'Enable TLS 1.3 for all connections',
'Deploy data loss prevention',
'Configure secure backup procedures'
],
priority: 'high'
},
{
control: 'NEN 7512 Exchange',
tasks: [
'Connect to national healthcare infrastructure',
'Implement secure messaging solution',
'Configure recipient verification',
'Enable patient consent management'
],
priority: 'high'
}
]
};Phase 3: Monitoring and Improvement
# phase3-monitoring.yml
continuous_monitoring:
access_monitoring:
- Real-time anomaly detection on patient data access
- Alerts for unusual access patterns
- Monthly access pattern reports
- Quarterly access reviews
security_monitoring:
- SIEM integration for security events
- Vulnerability scanning (weekly)
- Penetration testing (annual)
- Incident response drills (quarterly)
compliance_monitoring:
- Automated NEN 7513 log completeness checks
- NEN 7512 exchange verification
- Patient consent tracking
- Regulatory change monitoring
key_performance_indicators:
- Percentage of accesses with valid treatment relationship
- Time to detect unauthorised access attempts
- Log completeness rate (target: 100%)
- Patient access request response time
- Security incident response timeCommon Challenges and Solutions
Challenge 1: Legacy System Integration
// legacy-integration.ts
interface LegacyIntegrationStrategy {
system: string;
challenges: string[];
solutions: Solution[];
}
const legacyIntegrationStrategies: LegacyIntegrationStrategy[] = [
{
system: 'Legacy EPD/EHR without NEN 7513 logging',
challenges: [
'No built-in access logging',
'Limited API capabilities',
'Vendor no longer supported'
],
solutions: [
{
approach: 'Database-level logging',
implementation: `
Deploy database triggers to capture all SELECT/UPDATE
operations on patient tables. Forward to central log system.
`,
limitations: ['Cannot capture application-level context']
},
{
approach: 'Application proxy',
implementation: `
Route all application traffic through logging proxy
that captures access patterns and enriches with context.
`,
limitations: ['Additional latency', 'Complex configuration']
},
{
approach: 'Gradual migration',
implementation: `
Plan migration to compliant system. Document compensating
controls for interim period.
`,
timeline: '12-24 months'
}
]
}
];Challenge 2: Emergency Access (Break-the-Glass)
// break-the-glass.ts
interface EmergencyAccess {
accessId: string;
requestor: string;
patient: string;
reason: EmergencyReason;
justification: string;
approver?: string;
timestamp: Date;
expiresAt: Date;
accessedData: string[];
}
type EmergencyReason =
| 'medical_emergency'
| 'patient_unconscious'
| 'urgent_consultation'
| 'disaster_response';
const requestEmergencyAccess = async (
patientId: string,
reason: EmergencyReason,
justification: string
): Promise<EmergencyAccess> => {
const accessId = generateUUID();
const requestor = getCurrentUser();
// Log the emergency access request
const emergencyAccess: EmergencyAccess = {
accessId,
requestor: requestor.id,
patient: patientId,
reason,
justification,
timestamp: new Date(),
expiresAt: new Date(Date.now() + 4 * 60 * 60 * 1000), // 4 hours
accessedData: []
};
// Store and alert
await storeEmergencyAccess(emergencyAccess);
await alertSecurityTeam(emergencyAccess);
await alertPrivacyOfficer(emergencyAccess);
// Schedule mandatory review
await scheduleEmergencyAccessReview(emergencyAccess);
return emergencyAccess;
};
const reviewEmergencyAccess = async (
accessId: string,
reviewer: string,
decision: 'justified' | 'unjustified' | 'needs_investigation'
): Promise<void> => {
const access = await getEmergencyAccess(accessId);
await updateEmergencyAccess(accessId, {
reviewed: true,
reviewer,
reviewDecision: decision,
reviewDate: new Date()
});
if (decision === 'unjustified') {
await initiateInvestigation(access);
await notifyDataProtectionOfficer(access);
}
};Key Takeaways
-
Legal requirement: NEN 7510 compliance is mandatory for Dutch healthcare organisations
-
Patient-centric: Patient rights and access transparency are fundamental requirements
-
Comprehensive logging: NEN 7513 requires detailed logging of all patient data access
-
Secure exchange: Use approved methods (LSP, AORTA, MedMij) for health data exchange
-
Treatment relationship: Access should be based on verified care relationships
-
Emergency procedures: Implement break-the-glass with mandatory review
-
Integration with ISO 27001: NEN 7510 builds on ISO 27001 with healthcare specifics
-
Continuous monitoring: Real-time detection of unusual access patterns is essential
NEN 7510 compliance protects both patients and healthcare organisations. A well-implemented information security management system ensures patient trust while meeting legal obligations.