GDPR Technical Implementation Guide
The General Data Protection Regulation (GDPR) requires organizations to implement comprehensive data protection measures. This guide covers the technical implementation aspects, from data mapping to automated compliance systems.
GDPR Architecture Overview
┌─────────────────────────────────────────────────────────────────────────────┐
│ GDPR COMPLIANCE ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ DATA SUBJECT RIGHTS (Articles 15-22) │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Access │ │Rectify │ │ Erasure │ │Restrict │ │Portabil │ │ │
│ │ │Art. 15 │ │Art. 16 │ │Art. 17 │ │Art. 18 │ │Art. 20 │ │ │
│ │ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ │
│ │ │ │ │ │ │ │ │
│ │ └───────────┴───────────┼───────────┴───────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ DSAR MANAGEMENT │ │ │
│ │ │ SYSTEM │ │ │
│ │ └──────────┬──────────┘ │ │
│ │ │ │ │
│ └───────────────────────────────┼───────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────────┼───────────────────────────────────────┐ │
│ │ │ │ │
│ │ DATA PROCESSING ACTIVITIES │ │ │
│ │ ┌────────────────────────────┴────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │ │ │
│ │ │ │ CONSENT │ │ DATA │ │ LAWFUL │ │ │ │
│ │ │ │ MANAGEMENT │ │ MAPPING │ │ BASIS │ │ │ │
│ │ │ │ (Art. 7) │ │ (Art. 30) │ │ (Art. 6)│ │ │ │
│ │ │ └──────────────┘ └──────────────┘ └──────────┘ │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────────────────┐ │ │
│ │ │ DATA PROTECTION MEASURES │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ │ │
│ │ │ │Encryption│ │Pseudonym │ │ Access │ │ Security by │ │ │ │
│ │ │ │(Art. 32) │ │(Art. 4) │ │ Control │ │ Design (Art. 25) │ │ │ │
│ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────────────┘ │ │ │
│ │ │ │ │ │
│ │ └────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────────────┐│
│ │ COMPLIANCE DOCUMENTATION ││
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ ││
│ │ │Processing│ │ DPIA │ │ Breach │ │ DPA │ │ Privacy │ ││
│ │ │ Records │ │(Art. 35) │ │ Log │ │Contracts │ │ Notice │ ││
│ │ │(Art. 30) │ │ │ │(Art. 33) │ │(Art. 28) │ │ (Art. 13/14) │ ││
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ ││
│ └────────────────────────────────────────────────────────────────────────┘│
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Data Mapping Implementation
Data Inventory Structure
// Data mapping schema
interface DataInventoryRecord {
id: string;
dataCategory: string;
personalDataTypes: PersonalDataType[];
specialCategoryData: boolean;
// Processing details
processingPurposes: string[];
lawfulBasis: LawfulBasis;
consentRequired: boolean;
// Data subjects
dataSubjectCategories: string[];
estimatedVolume: number;
// Data sources and destinations
sources: DataSource[];
recipients: DataRecipient[];
thirdCountryTransfers: ThirdCountryTransfer[];
// Retention
retentionPeriod: string;
retentionJustification: string;
// Security
securityMeasures: string[];
encryptionAtRest: boolean;
encryptionInTransit: boolean;
pseudonymized: boolean;
// Systems
systems: string[];
databases: string[];
// Metadata
dataOwner: string;
lastReview: Date;
nextReview: Date;
}
type LawfulBasis =
| 'consent' // Article 6(1)(a)
| 'contract' // Article 6(1)(b)
| 'legal_obligation' // Article 6(1)(c)
| 'vital_interests' // Article 6(1)(d)
| 'public_task' // Article 6(1)(e)
| 'legitimate_interests'; // Article 6(1)(f)
interface ThirdCountryTransfer {
country: string;
recipient: string;
mechanism: 'adequacy_decision' | 'standard_clauses' | 'bcr' | 'derogation';
documentation: string;
}
Data Discovery Automation
#!/usr/bin/env python3
"""
Automated PII discovery for GDPR data mapping
Scans databases for personal data patterns
"""
import re
from dataclasses import dataclass
from typing import List, Dict
import sqlalchemy
@dataclass
class PIIPattern:
name: str
pattern: str
category: str
special_category: bool = False
# PII detection patterns
PII_PATTERNS = [
PIIPattern("email", r'^[\w\.-]+@[\w\.-]+\.\w+$', "contact"),
PIIPattern("phone", r'^\+?[\d\s\-\(\)]{10,}$', "contact"),
PIIPattern("ssn", r'^\d{3}-\d{2}-\d{4}$', "government_id"),
PIIPattern("credit_card", r'^\d{4}[\s\-]?\d{4}[\s\-]?\d{4}[\s\-]?\d{4}$', "financial"),
PIIPattern("ip_address", r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$', "technical"),
PIIPattern("date_of_birth", r'^\d{4}-\d{2}-\d{2}$', "personal"),
# Special category data
PIIPattern("health_condition", r'(?i)(diagnosis|medical|health|disease)', "health", True),
PIIPattern("religious", r'(?i)(religion|faith|worship|church|mosque)', "belief", True),
PIIPattern("political", r'(?i)(party|political|vote|election)', "political", True),
]
def scan_database(connection_string: str) -> Dict:
"""Scan database for PII columns"""
engine = sqlalchemy.create_engine(connection_string)
inspector = sqlalchemy.inspect(engine)
findings = []
for table_name in inspector.get_table_names():
columns = inspector.get_columns(table_name)
for column in columns:
col_name = column['name'].lower()
# Check column names for PII indicators
pii_indicators = ['email', 'phone', 'name', 'address', 'ssn',
'dob', 'birth', 'card', 'account']
for indicator in pii_indicators:
if indicator in col_name:
findings.append({
'table': table_name,
'column': column['name'],
'type': column['type'],
'pii_indicator': indicator,
'requires_review': True
})
# Sample data for pattern matching
with engine.connect() as conn:
for finding in findings:
query = f"SELECT DISTINCT {finding['column']} FROM {finding['table']} LIMIT 100"
try:
result = conn.execute(sqlalchemy.text(query))
samples = [str(row[0]) for row in result if row[0]]
# Match against PII patterns
for sample in samples[:10]:
for pattern in PII_PATTERNS:
if re.match(pattern.pattern, sample):
finding['confirmed_pii'] = pattern.name
finding['category'] = pattern.category
finding['special_category'] = pattern.special_category
break
except Exception as e:
finding['error'] = str(e)
return {
'scan_date': datetime.now().isoformat(),
'database': connection_string.split('@')[-1],
'findings': findings,
'summary': {
'tables_scanned': len(inspector.get_table_names()),
'pii_columns_found': len([f for f in findings if f.get('confirmed_pii')]),
'special_category_found': len([f for f in findings if f.get('special_category')])
}
}
if __name__ == '__main__':
import sys
results = scan_database(sys.argv[1])
print(json.dumps(results, indent=2))
Consent Management System
Consent Collection Implementation
// Consent management types
interface ConsentRecord {
id: string;
dataSubjectId: string;
// Consent details
purpose: string;
processingActivities: string[];
// Consent properties (GDPR Article 7 requirements)
givenAt: Date;
collectionMethod: 'explicit_checkbox' | 'double_opt_in' | 'written';
ipAddress?: string;
userAgent?: string;
// Consent text shown
consentTextVersion: string;
consentTextHash: string;
// Status
status: 'active' | 'withdrawn';
withdrawnAt?: Date;
// Evidence
evidence: ConsentEvidence;
}
interface ConsentEvidence {
screenshot?: string;
formSubmission: Record<string, any>;
timestamp: Date;
source: string;
}
// Consent management service
class ConsentService {
async recordConsent(
dataSubjectId: string,
consents: ConsentRequest[]
): Promise<ConsentRecord[]> {
const records: ConsentRecord[] = [];
for (const consent of consents) {
// Validate consent is freely given (no pre-checked boxes)
if (consent.preChecked) {
throw new Error('Pre-checked consent is not valid under GDPR');
}
// Validate consent is specific (one purpose per consent)
if (consent.purposes.length > 1) {
throw new Error('Consent must be specific to each purpose');
}
const record: ConsentRecord = {
id: generateUUID(),
dataSubjectId,
purpose: consent.purpose,
processingActivities: consent.processingActivities,
givenAt: new Date(),
collectionMethod: consent.method,
consentTextVersion: consent.textVersion,
consentTextHash: hashConsentText(consent.text),
status: 'active',
evidence: {
formSubmission: consent.formData,
timestamp: new Date(),
source: consent.source
}
};
await this.store.save(record);
records.push(record);
}
return records;
}
async withdrawConsent(
dataSubjectId: string,
consentId: string
): Promise<void> {
const consent = await this.store.findById(consentId);
if (consent.dataSubjectId !== dataSubjectId) {
throw new Error('Unauthorized');
}
consent.status = 'withdrawn';
consent.withdrawnAt = new Date();
await this.store.save(consent);
// Trigger downstream processing cessation
await this.processingService.stopProcessing(
dataSubjectId,
consent.processingActivities
);
}
async getConsentStatus(
dataSubjectId: string,
purpose: string
): Promise<boolean> {
const consent = await this.store.findActiveConsent(
dataSubjectId,
purpose
);
return consent?.status === 'active';
}
}
Cookie Consent Banner
// GDPR-compliant cookie consent implementation
interface CookieCategory {
id: string;
name: string;
description: string;
required: boolean; // Strictly necessary = required, no consent needed
cookies: CookieDefinition[];
}
interface CookieConsentConfig {
categories: CookieCategory[];
privacyPolicyUrl: string;
cookiePolicyUrl: string;
dpoContact: string;
}
const cookieConfig: CookieConsentConfig = {
categories: [
{
id: 'necessary',
name: 'Strictly Necessary',
description: 'Essential for the website to function. Cannot be disabled.',
required: true,
cookies: [
{ name: 'session_id', purpose: 'User session', duration: 'Session' },
{ name: 'csrf_token', purpose: 'Security', duration: 'Session' }
]
},
{
id: 'analytics',
name: 'Analytics',
description: 'Help us understand how visitors use our website.',
required: false,
cookies: [
{ name: '_ga', purpose: 'Google Analytics', duration: '2 years' },
{ name: '_gid', purpose: 'Google Analytics', duration: '24 hours' }
]
},
{
id: 'marketing',
name: 'Marketing',
description: 'Used to deliver relevant advertisements.',
required: false,
cookies: [
{ name: '_fbp', purpose: 'Facebook Pixel', duration: '90 days' }
]
}
],
privacyPolicyUrl: '/privacy',
cookiePolicyUrl: '/cookies',
dpoContact: '[email protected]'
};
// React component for cookie banner
function CookieConsentBanner() {
const [preferences, setPreferences] = useState<Record<string, boolean>>({});
const [showDetails, setShowDetails] = useState(false);
const handleAcceptAll = () => {
const allAccepted = Object.fromEntries(
cookieConfig.categories.map(c => [c.id, true])
);
savePreferences(allAccepted);
};
const handleRejectOptional = () => {
const onlyNecessary = Object.fromEntries(
cookieConfig.categories.map(c => [c.id, c.required])
);
savePreferences(onlyNecessary);
};
const handleSavePreferences = () => {
savePreferences(preferences);
};
return (
<div className="cookie-banner">
<h3>We use cookies</h3>
<p>
We use cookies to improve your experience. See our{' '}
<a href={cookieConfig.cookiePolicyUrl}>Cookie Policy</a>.
</p>
{showDetails && (
<div className="cookie-categories">
{cookieConfig.categories.map(category => (
<div key={category.id} className="category">
<label>
<input
type="checkbox"
checked={category.required || preferences[category.id]}
disabled={category.required}
onChange={(e) => setPreferences({
...preferences,
[category.id]: e.target.checked
})}
/>
<strong>{category.name}</strong>
{category.required && <span>(Required)</span>}
</label>
<p>{category.description}</p>
</div>
))}
</div>
)}
<div className="actions">
<button onClick={handleRejectOptional}>
Reject Optional
</button>
<button onClick={() => setShowDetails(!showDetails)}>
Manage Preferences
</button>
<button onClick={handleAcceptAll} className="primary">
Accept All
</button>
{showDetails && (
<button onClick={handleSavePreferences}>
Save Preferences
</button>
)}
</div>
</div>
);
}
DSAR (Data Subject Access Request) System
DSAR Workflow
┌─────────────────────────────────────────────────────────────────────────────┐
│ DSAR PROCESSING WORKFLOW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 1. REQUEST │────▶│ 2. VERIFY │────▶│ 3. SEARCH │ │
│ │ RECEIVED │ │ IDENTITY │ │ DATA │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ │ │ │ │
│ Day 0: Clock Day 1-3: ID Day 3-20: Gather │
│ starts verification data from all │
│ systems │
│ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 4. REVIEW │────▶│ 5. PREPARE │────▶│ 6. DELIVER │ │
│ │ & REDACT │ │ RESPONSE │ │ RESPONSE │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ │ │ │ │
│ Day 20-25: Day 25-28: Day 28-30: │
│ Review for Format data, Secure delivery │
│ third-party generate report to data subject │
│ data, exemptions │
│ │
│ ─────────────────────────────────────────────────────────────────────────│
│ TIMELINE: 30 calendar days (extendable by 60 days for complex requests) │
│ ─────────────────────────────────────────────────────────────────────────│
│ │
└─────────────────────────────────────────────────────────────────────────────┘
DSAR Automation Implementation
// DSAR request types
interface DSARRequest {
id: string;
dataSubjectId: string;
requestType: DSARType;
receivedAt: Date;
deadline: Date;
status: DSARStatus;
// Identity verification
identityVerified: boolean;
verificationMethod?: string;
verifiedAt?: Date;
// Processing
assignedTo?: string;
notes: string[];
// Response
responseData?: DSARResponse;
deliveredAt?: Date;
}
type DSARType =
| 'access' // Article 15 - Right of access
| 'rectification' // Article 16 - Right to rectification
| 'erasure' // Article 17 - Right to erasure
| 'restriction' // Article 18 - Right to restriction
| 'portability' // Article 20 - Right to data portability
| 'objection'; // Article 21 - Right to object
type DSARStatus =
| 'received'
| 'identity_verification'
| 'in_progress'
| 'review'
| 'response_ready'
| 'completed'
| 'rejected';
// DSAR processing service
class DSARService {
async submitRequest(
email: string,
requestType: DSARType,
details: string
): Promise<DSARRequest> {
// Find or create data subject
const dataSubject = await this.findOrCreateDataSubject(email);
const deadline = this.calculateDeadline(new Date());
const request: DSARRequest = {
id: generateUUID(),
dataSubjectId: dataSubject.id,
requestType,
receivedAt: new Date(),
deadline,
status: 'received',
identityVerified: false,
notes: [details]
};
await this.store.save(request);
// Send verification email
await this.sendVerificationEmail(dataSubject.email, request.id);
// Notify DPO/privacy team
await this.notifyPrivacyTeam(request);
return request;
}
async processAccessRequest(requestId: string): Promise<DSARResponse> {
const request = await this.store.findById(requestId);
if (!request.identityVerified) {
throw new Error('Identity not verified');
}
// Collect data from all sources
const dataCollections: DataCollection[] = [];
// 1. Main database
dataCollections.push(
await this.collectFromDatabase(request.dataSubjectId)
);
// 2. Analytics systems
dataCollections.push(
await this.collectFromAnalytics(request.dataSubjectId)
);
// 3. Support tickets
dataCollections.push(
await this.collectFromSupport(request.dataSubjectId)
);
// 4. Third-party processors
for (const processor of this.processors) {
dataCollections.push(
await processor.collectData(request.dataSubjectId)
);
}
// Compile response
const response: DSARResponse = {
requestId,
generatedAt: new Date(),
dataSubjectInfo: await this.getDataSubjectInfo(request.dataSubjectId),
// Article 15(1) requirements
processingPurposes: this.getProcessingPurposes(),
dataCategories: this.extractCategories(dataCollections),
recipients: this.getRecipients(),
retentionPeriods: this.getRetentionInfo(),
dataSubjectRights: this.getRightsInfo(),
sourceOfData: this.getDataSources(dataCollections),
automatedDecisionMaking: this.getAutomatedDecisions(),
// Actual data
data: dataCollections,
// Third country transfers
thirdCountryTransfers: this.getTransferInfo()
};
return response;
}
async processErasureRequest(requestId: string): Promise<void> {
const request = await this.store.findById(requestId);
// Check for exemptions
const exemptions = await this.checkErasureExemptions(
request.dataSubjectId
);
if (exemptions.length > 0) {
// Partial erasure - document what can't be deleted and why
request.notes.push(
`Exemptions found: ${exemptions.join(', ')}`
);
}
// Delete from all systems
const deletionLog: DeletionRecord[] = [];
// 1. Main database
deletionLog.push(
await this.deleteFromDatabase(request.dataSubjectId, exemptions)
);
// 2. Backups (schedule for deletion per retention policy)
await this.scheduleBackupDeletion(request.dataSubjectId);
// 3. Third-party processors
for (const processor of this.processors) {
deletionLog.push(
await processor.deleteData(request.dataSubjectId)
);
}
// 4. Analytics (anonymize rather than delete if needed for statistics)
await this.anonymizeAnalytics(request.dataSubjectId);
// Document deletion
request.notes.push(`Deletion completed: ${JSON.stringify(deletionLog)}`);
request.status = 'completed';
await this.store.save(request);
}
private calculateDeadline(receivedDate: Date): Date {
// 30 calendar days from receipt
const deadline = new Date(receivedDate);
deadline.setDate(deadline.getDate() + 30);
return deadline;
}
private async checkErasureExemptions(
dataSubjectId: string
): Promise<string[]> {
const exemptions: string[] = [];
// Article 17(3) exemptions
// (a) Freedom of expression and information
// (b) Legal obligation
if (await this.hasLegalRetentionRequirement(dataSubjectId)) {
exemptions.push('legal_obligation');
}
// (c) Public health
// (d) Archiving in public interest
// (e) Legal claims
if (await this.hasOngoingLegalMatter(dataSubjectId)) {
exemptions.push('legal_claims');
}
return exemptions;
}
}
Privacy by Design Implementation
Data Minimization Patterns
// Privacy by Design: Data minimization in API responses
interface User {
id: string;
email: string;
passwordHash: string;
name: string;
dateOfBirth: Date;
address: Address;
phoneNumber: string;
createdAt: Date;
lastLogin: Date;
internalNotes: string;
}
// Different views based on purpose and audience
interface UserPublicProfile {
id: string;
name: string;
// Only what's necessary for public display
}
interface UserSelfView {
id: string;
email: string;
name: string;
dateOfBirth: Date;
address: Address;
phoneNumber: string;
createdAt: Date;
lastLogin: Date;
// User can see their own data (minus internal fields)
}
interface UserAdminView extends User {
// Admins see everything for legitimate purposes
}
// View transformer with purpose validation
class UserViewTransformer {
toPublicProfile(user: User): UserPublicProfile {
return {
id: user.id,
name: user.name
};
}
toSelfView(user: User, requestingUserId: string): UserSelfView {
// Verify the user is requesting their own data
if (user.id !== requestingUserId) {
throw new Error('Unauthorized');
}
const { passwordHash, internalNotes, ...selfView } = user;
return selfView;
}
toAdminView(user: User, admin: Admin, purpose: string): UserAdminView {
// Log access for audit trail
this.auditLog.record({
accessedBy: admin.id,
accessedData: user.id,
purpose,
timestamp: new Date()
});
return user;
}
}
Pseudonymization Implementation
// Pseudonymization service for GDPR compliance
interface PseudonymizationConfig {
algorithm: 'sha256' | 'hmac-sha256';
secret: string; // For reversible pseudonymization
salt?: string;
}
class PseudonymizationService {
constructor(private config: PseudonymizationConfig) {}
// One-way pseudonymization (irreversible)
pseudonymize(identifier: string): string {
const hash = crypto.createHash('sha256');
hash.update(identifier + (this.config.salt || ''));
return hash.digest('hex');
}
// Reversible pseudonymization (when re-identification needed)
pseudonymizeReversible(identifier: string): string {
const hmac = crypto.createHmac('sha256', this.config.secret);
hmac.update(identifier);
return hmac.digest('hex');
}
// Generate consistent pseudonym for same input
generatePseudonym(identifier: string, context: string): string {
// Context-specific pseudonyms prevent correlation
const hash = crypto.createHash('sha256');
hash.update(`${context}:${identifier}:${this.config.salt}`);
return `PSEUDO_${hash.digest('hex').substring(0, 16)}`;
}
// Pseudonymize dataset for analytics
pseudonymizeDataset(
records: Record<string, any>[],
fieldsToMask: string[]
): Record<string, any>[] {
return records.map(record => {
const masked = { ...record };
for (const field of fieldsToMask) {
if (masked[field]) {
masked[field] = this.pseudonymize(String(masked[field]));
}
}
return masked;
});
}
}
// Usage example: Analytics with pseudonymized data
async function generateAnalyticsReport(): Promise<Report> {
const pseudoService = new PseudonymizationService(config);
// Fetch user activity data
const rawData = await database.query(`
SELECT user_id, action, timestamp, ip_address
FROM user_activity
WHERE timestamp > NOW() - INTERVAL '30 days'
`);
// Pseudonymize before analysis
const pseudonymizedData = pseudoService.pseudonymizeDataset(
rawData,
['user_id', 'ip_address'] // Fields containing personal data
);
// Now safe to analyze without identifying individuals
return analyzeActivityPatterns(pseudonymizedData);
}
Data Protection Impact Assessment (DPIA)
┌─────────────────────────────────────────────────────────────────────────────┐
│ DPIA PROCESS WORKFLOW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ STEP 1: SCREENING - Is DPIA Required? │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ DPIA required if processing is likely to result in HIGH RISK: │ │
│ │ │ │
│ │ ☐ Systematic profiling with significant effects │ │
│ │ ☐ Large-scale processing of special categories │ │
│ │ ☐ Systematic monitoring of public areas │ │
│ │ ☐ Automated decision-making with legal effects │ │
│ │ ☐ New technologies │ │
│ │ ☐ Denies data subjects a right/service/contract │ │
│ │ ☐ Large-scale profiling │ │
│ │ ☐ Processing biometric/genetic data │ │
│ │ ☐ Cross-border processing │ │
│ │ │ │
│ │ 2 or more criteria checked = DPIA required │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ STEP 2: DESCRIBE THE PROCESSING │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ • Nature of processing (what operations?) │ │
│ │ • Scope (how much data, how many subjects, geographic area?) │ │
│ │ • Context (relationship with data subjects, expectations) │ │
│ │ • Purpose (why processing this data?) │ │
│ │ • Data flows (collection → storage → use → sharing → deletion) │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ STEP 3: ASSESS NECESSITY AND PROPORTIONALITY │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ • Is processing necessary for the purpose? │ │
│ │ • Could the purpose be achieved with less data? │ │
│ │ • What is the lawful basis? │ │
│ │ • How is data quality ensured? │ │
│ │ • What are retention periods? │ │
│ │ • How are data subject rights facilitated? │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ STEP 4: IDENTIFY AND ASSESS RISKS │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Risk Type │ Likelihood │ Severity │ Overall Risk │ │
│ │ ───────────────────┼────────────┼──────────┼─────────────────────── │ │
│ │ Unauthorized access│ Medium │ High │ High │ │
│ │ Data breach │ Low │ High │ Medium │ │
│ │ Function creep │ Medium │ Medium │ Medium │ │
│ │ Re-identification │ Low │ High │ Medium │ │
│ │ Discrimination │ Low │ V.High │ High │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ STEP 5: IDENTIFY MEASURES TO MITIGATE RISKS │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Risk │ Mitigation Measure │ Residual │ │
│ │ ───────────────────┼───────────────────────────────────┼─────────── │ │
│ │ Unauthorized access│ MFA, encryption, access controls │ Low │ │
│ │ Data breach │ Encryption, monitoring, IR plan │ Low │ │
│ │ Function creep │ Purpose limitation, audits │ Low │ │
│ │ Re-identification │ Pseudonymization, aggregation │ Low │ │
│ │ Discrimination │ Bias testing, human oversight │ Medium │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ STEP 6: SIGN OFF AND RECORD │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ • DPO review and advice │ │
│ │ • Management sign-off │ │
│ │ • Document decision and rationale │ │
│ │ • Publish DPIA (if appropriate) │ │
│ │ • Schedule review (annually or when processing changes) │ │
│ │ • Consult supervisory authority if high risk remains │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Breach Notification System
// Data breach management system
interface DataBreach {
id: string;
detectedAt: Date;
reportingDeadline: Date; // 72 hours from detection
// Breach details
description: string;
affectedDataTypes: string[];
affectedDataSubjectCount: number;
affectedDataSubjectCategories: string[];
// Risk assessment
riskLevel: 'low' | 'medium' | 'high';
likelyConsequences: string[];
// Measures taken
containmentMeasures: string[];
mitigationMeasures: string[];
// Notifications
supervisoryAuthorityNotified: boolean;
supervisoryAuthorityNotifiedAt?: Date;
dataSubjectsNotified: boolean;
dataSubjectsNotifiedAt?: Date;
// Documentation
internalReportUrl: string;
status: 'detected' | 'contained' | 'reported' | 'closed';
}
class BreachNotificationService {
async reportBreach(breachDetails: Partial<DataBreach>): Promise<DataBreach> {
const breach: DataBreach = {
id: generateUUID(),
detectedAt: new Date(),
reportingDeadline: this.calculate72HourDeadline(),
status: 'detected',
supervisoryAuthorityNotified: false,
dataSubjectsNotified: false,
...breachDetails
} as DataBreach;
// Immediately notify incident response team
await this.notifyIncidentTeam(breach);
// Assess if supervisory authority notification required
if (this.requiresAuthorityNotification(breach)) {
await this.scheduleAuthorityNotification(breach);
}
// Assess if data subject notification required
if (this.requiresDataSubjectNotification(breach)) {
await this.scheduleDataSubjectNotification(breach);
}
await this.store.save(breach);
return breach;
}
private requiresAuthorityNotification(breach: DataBreach): boolean {
// Article 33: Notify unless "unlikely to result in a risk"
// Most breaches require notification to be safe
return breach.riskLevel !== 'low';
}
private requiresDataSubjectNotification(breach: DataBreach): boolean {
// Article 34: Notify if "likely to result in a high risk"
return breach.riskLevel === 'high';
}
async generateAuthorityNotification(breach: DataBreach): Promise<string> {
// Article 33(3) required content
return `
DATA BREACH NOTIFICATION TO SUPERVISORY AUTHORITY
1. Nature of the breach:
${breach.description}
2. Categories and approximate number of data subjects:
Categories: ${breach.affectedDataSubjectCategories.join(', ')}
Approximate number: ${breach.affectedDataSubjectCount}
3. Categories and approximate number of data records:
${breach.affectedDataTypes.join(', ')}
4. Contact details of DPO:
${this.dpoContact}
5. Likely consequences:
${breach.likelyConsequences.join('\n')}
6. Measures taken/proposed:
Containment: ${breach.containmentMeasures.join('\n')}
Mitigation: ${breach.mitigationMeasures.join('\n')}
Reported within 72 hours: ${
breach.detectedAt <= breach.reportingDeadline ? 'Yes' : 'No'
}
`;
}
private calculate72HourDeadline(): Date {
const deadline = new Date();
deadline.setHours(deadline.getHours() + 72);
return deadline;
}
}
Related Resources
- Compliance Frameworks Complete Guide - Hub article
- HIPAA Compliance - US healthcare
- SOC 2 Compliance - Service organizations
- Continuous Compliance Monitoring - Ongoing compliance
Tools
- Security Policy Generator - Create GDPR-compliant policies
- Privacy Policy Generator - Generate privacy notices