AWS Security: AVG Compliance Checklist
Een uitgebreide gids voor het implementeren van AVG-compliant AWS infrastructuur voor Nederlandse organisaties
Introductie
De Algemene Verordening Gegevensbescherming (AVG) heeft fundamenteel veranderd hoe organisaties omgaan met persoonsgegevens in de Europese Unie. Voor bedrijven die AWS gebruiken, vereist het bereiken en handhaven van AVG compliance een systematische aanpak die AWS security services, juiste architectuur en operationele processen combineert.
Deze gids biedt een praktische checklist voor het implementeren van AVG compliance op AWS, met code voorbeelden en automatiseringsscripts specifiek afgestemd op Nederlandse organisaties.
AVG Begrijpen op AWS
Het Gedeelde Verantwoordelijkheidsmodel
AWS Verantwoordelijkheid (Security OF de Cloud):
- Fysieke infrastructuur beveiliging
- Netwerk infrastructuur
- Hypervisor en hardware
- AWS services compliance certificeringen
Jouw Verantwoordelijkheid (Security IN de Cloud):
- Data encryptie en bescherming
- Toegangsbeheer
- Netwerk security configuratie
- Compliance met AVG vereisten
- Implementatie van rechten van betrokkenen
Belangrijke AVG Principes voor AWS
- Rechtmatigheid, Behoorlijkheid en Transparantie
- Doelbinding
- Minimale Gegevensverwerking
- Juistheid
- Opslagbeperking
- Integriteit en Vertrouwelijkheid
- Verantwoordingsplicht
AVG Compliance Checklist
1. Data Ontdekking en Classificatie
Identificeer Persoonsgegevens:
- Breng alle dataopslag met persoonsgegevens in kaart
- Classificeer data gevoeligheidsniveaus
- Documenteer datastromen en verwerkingsactiviteiten
import boto3
import json
# Gebruik AWS Macie voor geautomatiseerde data ontdekking
macie = boto3.client('macie2', region_name='eu-west-1')
# Creëer classificatie job voor S3 buckets
def creeer_classificatie_job():
response = macie.create_classification_job(
jobType='ONE_TIME',
name='AVG-Data-Ontdekking',
s3JobDefinition={
'bucketDefinitions': [
{
'accountId': '123456789012',
'buckets': ['klantdata-bucket', 'analytics-bucket']
}
]
},
customDataIdentifierIds=[],
managedDataIdentifierSelector='ALL',
tags={
'Doel': 'AVG-Compliance',
'DataClassificatie': 'Persoonsgegevens'
}
)
return response['jobId']
job_id = creeer_classificatie_job()
print(f"Classificatie job aangemaakt: {job_id}")
Tag Resources met Data Classificatie:
import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';
// Creëer bucket met AVG classificatie tags
const klantdataBucket = new s3.Bucket(this, 'KlantData', {
encryption: s3.BucketEncryption.S3_MANAGED,
versioned: true,
lifecycleRules: [
{
expiration: cdk.Duration.days(365),
noncurrentVersionExpiration: cdk.Duration.days(30),
},
],
});
cdk.Tags.of(klantdataBucket).add('DataClassificatie', 'Persoonlijk');
cdk.Tags.of(klantdataBucket).add('AVG', 'Ja');
cdk.Tags.of(klantdataBucket).add('DataLocatie', 'EU');
2. Data Residency en Lokalisatie
Forceer EU-Only Regio’s:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "WeigerNietEURegios",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": [
"eu-west-1",
"eu-west-2",
"eu-west-3",
"eu-central-1",
"eu-north-1"
]
}
}
}
]
}
Implementeer met AWS Organizations SCP:
# Creëer Service Control Policy voor data residency
aws organizations create-policy \
--name "EU-Data-Residency-Policy" \
--description "Beperk resources tot EU regio's alleen" \
--type SERVICE_CONTROL_POLICY \
--content file://eu-regio-policy.json
# Koppel aan organizational unit
aws organizations attach-policy \
--policy-id p-xxxxxxxx \
--target-id ou-xxxx-xxxxxxxx
Valideer Regio Compliance:
import boto3
def controleer_niet_eu_resources():
"""Controleer resources buiten EU regio's"""
eu_regios = [
'eu-west-1', 'eu-west-2', 'eu-west-3',
'eu-central-1', 'eu-north-1'
]
ec2 = boto3.client('ec2')
alle_regios = [regio['RegionName'] for regio in ec2.describe_regions()['Regions']]
overtredingen = []
for regio in alle_regios:
if regio not in eu_regios:
ec2_regionaal = boto3.client('ec2', region_name=regio)
instances = ec2_regionaal.describe_instances()
for reservation in instances['Reservations']:
for instance in reservation['Instances']:
overtredingen.append({
'ResourceId': instance['InstanceId'],
'Regio': regio,
'Type': 'EC2 Instance'
})
return overtredingen
# Voer audit uit
overtredingen = controleer_niet_eu_resources()
if overtredingen:
print(f"WAARSCHUWING: {len(overtredingen)} resources gevonden buiten EU regio's")
for overtreding in overtredingen:
print(f" - {overtreding['Type']} {overtreding['ResourceId']} in {overtreding['Regio']}")
3. Encryptie in Rust
S3 Bucket Encryptie:
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as kms from 'aws-cdk-lib/aws-kms';
// Creëer KMS key voor data encryptie
const dataEncryptieKey = new kms.Key(this, 'DataEncryptieKey', {
enableKeyRotation: true,
description: 'KMS key voor AVG-conforme data encryptie',
alias: 'avg-data-encryptie',
});
// Creëer versleutelde bucket
const beveiligdeDataBucket = new s3.Bucket(this, 'BeveiligdeData', {
encryption: s3.BucketEncryption.KMS,
encryptionKey: dataEncryptieKey,
bucketKeyEnabled: true,
versioned: true,
enforceSSL: true,
});
RDS Database Encryptie:
import * as rds from 'aws-cdk-lib/aws-rds';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
const database = new rds.DatabaseInstance(this, 'KlantDatabase', {
engine: rds.DatabaseInstanceEngine.postgres({
version: rds.PostgresEngineVersion.VER_15_3,
}),
instanceType: ec2.InstanceType.of(
ec2.InstanceClass.T4G,
ec2.InstanceSize.MEDIUM
),
vpc,
storageEncrypted: true,
storageEncryptionKey: dataEncryptieKey,
backupRetention: cdk.Duration.days(35),
deletionProtection: true,
cloudwatchLogsExports: ['postgresql'],
});
Forceer Encryptie met AWS Config:
Resources:
S3BucketEncryptionRule:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: s3-bucket-server-side-encryption-enabled
Source:
Owner: AWS
SourceIdentifier: S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED
Scope:
ComplianceResourceTypes:
- AWS::S3::Bucket
RDSEncryptionRule:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: rds-storage-encrypted
Source:
Owner: AWS
SourceIdentifier: RDS_STORAGE_ENCRYPTED
Scope:
ComplianceResourceTypes:
- AWS::RDS::DBInstance
4. Encryptie tijdens Transport
Forceer HTTPS/TLS:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "WeigerOnveiligTransport",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::klantdata-bucket/*",
"arn:aws:s3:::klantdata-bucket"
],
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
}
]
}
Application Load Balancer met TLS:
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import * as acm from 'aws-cdk-lib/aws-certificatemanager';
// Creëer ACM certificaat
const certificaat = new acm.Certificate(this, 'Certificaat', {
domainName: 'app.voorbeeld.nl',
validation: acm.CertificateValidation.fromDns(),
});
// Creëer ALB met HTTPS listener
const alb = new elbv2.ApplicationLoadBalancer(this, 'ALB', {
vpc,
internetFacing: true,
});
const httpsListener = alb.addListener('HttpsListener', {
port: 443,
certificates: [certificaat],
sslPolicy: elbv2.SslPolicy.TLS13_RES,
});
// Redirect HTTP naar HTTPS
const httpListener = alb.addListener('HttpListener', {
port: 80,
defaultAction: elbv2.ListenerAction.redirect({
protocol: 'HTTPS',
port: '443',
permanent: true,
}),
});
5. Toegangscontrole en Authenticatie
Implementeer Least Privilege IAM:
import * as iam from 'aws-cdk-lib/aws-iam';
// Creëer role met minimale permissies
const dataVerwerkerRole = new iam.Role(this, 'DataVerwerkerRole', {
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
description: 'Role voor AVG-conforme dataverwerking',
});
// Voeg specifieke permissies toe
dataVerwerkerRole.addToPolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
's3:GetObject',
's3:PutObject',
],
resources: ['arn:aws:s3:::klantdata-bucket/verwerkt/*'],
conditions: {
'StringEquals': {
's3:x-amz-server-side-encryption': 'aws:kms',
},
},
}));
Schakel MFA in voor Gevoelige Operaties:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VereistMFAVoorDataVerwijdering",
"Effect": "Deny",
"Action": [
"s3:DeleteObject",
"s3:DeleteBucket",
"rds:DeleteDBInstance"
],
"Resource": "*",
"Condition": {
"BoolIfExists": {
"aws:MultiFactorAuthPresent": "false"
}
}
}
]
}
Implementeer SSO met SAML:
import * as iam from 'aws-cdk-lib/aws-iam';
// Creëer SAML provider voor Azure AD integratie
const samlProvider = new iam.SamlProvider(this, 'AzureADProvider', {
metadataDocument: iam.SamlMetadataDocument.fromFile('./azure-ad-metadata.xml'),
name: 'AzureAD',
});
// Creëer role voor gefedereerde gebruikers
const gefedereerdeRole = new iam.Role(this, 'GefedereerdeGebruikerRole', {
assumedBy: new iam.SamlConsolePrincipal(samlProvider),
managedPolicies: [
iam.ManagedPolicy.fromAwsManagedPolicyName('ReadOnlyAccess'),
],
});
6. Audit Logging en Monitoring
Schakel Uitgebreide Logging in:
import * as cloudtrail from 'aws-cdk-lib/aws-cloudtrail';
import * as s3 from 'aws-cdk-lib/aws-s3';
// Creëer logging bucket
const logBucket = new s3.Bucket(this, 'AuditLogBucket', {
encryption: s3.BucketEncryption.S3_MANAGED,
versioned: true,
lifecycleRules: [
{
transitions: [
{
storageClass: s3.StorageClass.GLACIER,
transitionAfter: cdk.Duration.days(90),
},
],
expiration: cdk.Duration.days(2555), // 7 jaar voor compliance
},
],
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
});
// Schakel CloudTrail in
const trail = new cloudtrail.Trail(this, 'AVGAuditTrail', {
bucket: logBucket,
enableFileValidation: true,
includeGlobalServiceEvents: true,
isMultiRegionTrail: true,
managementEvents: cloudtrail.ReadWriteType.ALL,
});
// Log data events voor gevoelige buckets
trail.addS3EventSelector([{
bucket: klantdataBucket,
objectPrefix: '',
}], {
readWriteType: cloudtrail.ReadWriteType.ALL,
includeManagementEvents: true,
});
Geautomatiseerde Compliance Monitoring:
import boto3
from datetime import datetime, timedelta
def monitor_data_toegang():
"""Monitor toegang tot persoonsgegevens"""
cloudtrail = boto3.client('cloudtrail', region_name='eu-west-1')
# Query CloudTrail voor data toegang events
response = cloudtrail.lookup_events(
LookupAttributes=[
{
'AttributeKey': 'ResourceType',
'AttributeValue': 'AWS::S3::Object'
}
],
StartTime=datetime.now() - timedelta(hours=24),
MaxResults=50
)
verdachte_toegang = []
for event in response['Events']:
event_naam = event['EventName']
gebruikersnaam = event.get('Username', 'Onbekend')
# Markeer ongebruikelijke toegangspatronen
if event_naam in ['DeleteObject', 'GetObject'] and 'klantdata' in str(event):
verdachte_toegang.append({
'Tijd': event['EventTime'],
'Gebruiker': gebruikersnaam,
'Actie': event_naam,
'Resource': event.get('Resources', [{}])[0].get('ResourceName')
})
return verdachte_toegang
# Voer monitoring uit
alerts = monitor_data_toegang()
if alerts:
print(f"ALERT: {len(alerts)} verdachte data toegang events gedetecteerd")
for alert in alerts:
print(f" {alert['Tijd']}: {alert['Gebruiker']} voerde {alert['Actie']} uit")
7. Implementatie van Rechten van Betrokkenen
Recht op Inzage (Artikel 15):
import boto3
import json
from datetime import datetime
def exporteer_gebruikersdata(gebruiker_email):
"""Exporteer alle data voor een specifieke gebruiker"""
dynamodb = boto3.resource('dynamodb', region_name='eu-west-1')
s3 = boto3.client('s3', region_name='eu-west-1')
# Query gebruikersdata uit DynamoDB
tabel = dynamodb.Table('GebruikersData')
response = tabel.query(
KeyConditionExpression='email = :email',
ExpressionAttributeValues={
':email': gebruiker_email
}
)
gebruikersdata = {
'persoonlijke_informatie': response['Items'],
'export_datum': datetime.now().isoformat(),
'verzoek_type': 'AVG Artikel 15 - Recht op Inzage'
}
# Upload naar beveiligde export bucket
export_key = f"avg-exports/{gebruiker_email}/{datetime.now().date()}.json"
s3.put_object(
Bucket='avg-export-bucket',
Key=export_key,
Body=json.dumps(gebruikersdata, indent=2),
ServerSideEncryption='aws:kms'
)
# Genereer presigned URL geldig voor 7 dagen
url = s3.generate_presigned_url(
'get_object',
Params={'Bucket': 'avg-export-bucket', 'Key': export_key},
ExpiresIn=604800
)
return url
Recht op Vergetelheid (Artikel 17):
import uuid
def verwijder_gebruikersdata(gebruiker_email):
"""Verwijder alle persoonsgegevens van een gebruiker"""
dynamodb = boto3.resource('dynamodb', region_name='eu-west-1')
s3 = boto3.client('s3', region_name='eu-west-1')
# Log verwijderingsverzoek voor audit
log_verwijderingsverzoek(gebruiker_email)
# Verwijder uit DynamoDB
tabel = dynamodb.Table('GebruikersData')
response = tabel.query(
KeyConditionExpression='email = :email',
ExpressionAttributeValues={':email': gebruiker_email}
)
with tabel.batch_writer() as batch:
for item in response['Items']:
batch.delete_item(Key={'email': gebruiker_email, 'timestamp': item['timestamp']})
# Verwijder S3 objecten
prefix = f"gebruikers/{gebruiker_email}/"
objecten = s3.list_objects_v2(Bucket='klantdata-bucket', Prefix=prefix)
if 'Contents' in objecten:
delete_keys = [{'Key': obj['Key']} for obj in objecten['Contents']]
s3.delete_objects(
Bucket='klantdata-bucket',
Delete={'Objects': delete_keys}
)
return {
'status': 'voltooid',
'gebruiker': gebruiker_email,
'verwijderdatum': datetime.now().isoformat()
}
def log_verwijderingsverzoek(gebruiker_email):
"""Log verwijderingsverzoek voor audit trail"""
dynamodb = boto3.resource('dynamodb', region_name='eu-west-1')
audit_tabel = dynamodb.Table('AVGAuditLog')
audit_tabel.put_item(Item={
'verzoek_id': str(uuid.uuid4()),
'gebruiker_email': gebruiker_email,
'verzoek_type': 'RECHT_OP_VERGETELHEID',
'timestamp': datetime.now().isoformat(),
'status': 'IN_BEHANDELING'
})
Recht op Dataportabiliteit (Artikel 20):
def exporteer_portabele_data(gebruiker_email):
"""Exporteer gebruikersdata in machine-leesbaar formaat"""
dynamodb = boto3.resource('dynamodb', region_name='eu-west-1')
tabel = dynamodb.Table('GebruikersData')
response = tabel.query(
KeyConditionExpression='email = :email',
ExpressionAttributeValues={':email': gebruiker_email}
)
# Converteer naar portabel JSON formaat
portabele_data = {
'betrokkene': gebruiker_email,
'export_formaat': 'JSON',
'export_datum': datetime.now().isoformat(),
'data': response['Items']
}
return json.dumps(portabele_data, indent=2)
8. Data Retentie en Lifecycle Management
Geautomatiseerde Data Retentie:
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
// S3 lifecycle voor geautomatiseerde verwijdering
const avgBucket = new s3.Bucket(this, 'AVGDataBucket', {
lifecycleRules: [
{
id: 'VerwijderOudeKlantdata',
enabled: true,
prefix: 'klantdata/',
expiration: cdk.Duration.days(365), // 1 jaar retentie
noncurrentVersionExpiration: cdk.Duration.days(30),
},
{
id: 'ArchiveerAnalytics',
enabled: true,
prefix: 'analytics/',
transitions: [
{
storageClass: s3.StorageClass.GLACIER,
transitionAfter: cdk.Duration.days(90),
},
],
expiration: cdk.Duration.days(730), // 2 jaar
},
],
});
// DynamoDB met TTL voor automatische vervaldatum
const gebruikersData = new dynamodb.Table(this, 'GebruikersData', {
partitionKey: { name: 'email', type: dynamodb.AttributeType.STRING },
sortKey: { name: 'timestamp', type: dynamodb.AttributeType.NUMBER },
timeToLiveAttribute: 'ttl',
pointInTimeRecovery: true,
encryption: dynamodb.TableEncryption.AWS_MANAGED,
});
Retentiebeleid Enforcement:
import boto3
from datetime import datetime, timedelta
def forceer_retentiebeleid():
"""Forceer data retentie beleid"""
dynamodb = boto3.resource('dynamodb', region_name='eu-west-1')
tabel = dynamodb.Table('GebruikersData')
# Bereken TTL (1 jaar vanaf nu)
ttl = int((datetime.now() + timedelta(days=365)).timestamp())
# Update items met TTL
response = tabel.scan()
with tabel.batch_writer() as batch:
for item in response['Items']:
if 'ttl' not in item:
item['ttl'] = ttl
batch.put_item(Item=item)
print(f"{len(response['Items'])} items bijgewerkt met retentiebeleid")
9. Datalek Detectie en Respons
Geautomatiseerde Datalek Detectie:
import * as guardduty from 'aws-cdk-lib/aws-guardduty';
import * as sns from 'aws-cdk-lib/aws-sns';
import * as events from 'aws-cdk-lib/aws-events';
import * as targets from 'aws-cdk-lib/aws-events-targets';
// Schakel GuardDuty in
const detector = new guardduty.CfnDetector(this, 'AVGThreatDetector', {
enable: true,
dataSources: {
s3Logs: { enable: true },
kubernetes: { auditLogs: { enable: true } },
},
});
// Creëer SNS topic voor datalek alerts
const datalekAlertTopic = new sns.Topic(this, 'DatalekAlertTopic', {
displayName: 'AVG Datalek Alerts',
});
// EventBridge rule voor hoge severity bevindingen
const datalekRule = new events.Rule(this, 'DatalekDetectieRule', {
eventPattern: {
source: ['aws.guardduty'],
detailType: ['GuardDuty Finding'],
detail: {
severity: [7, 8, 9], // Alleen High en Critical
},
},
});
datalekRule.addTarget(new targets.SnsTopic(datalekAlertTopic));
Datalek Respons Lambda:
import boto3
import os
from datetime import datetime
def lambda_handler(event, context):
"""Geautomatiseerde AVG datalek respons"""
# Parse GuardDuty bevinding
bevinding = event['detail']
ernst = bevinding['severity']
bevinding_type = bevinding['type']
# Log incident
log_security_incident(bevinding)
# Informeer FG (Functionaris Gegevensbescherming) bij hoge ernst
if ernst >= 7:
informeer_fg(bevinding)
# Start 72-uur meldingsproces
start_datalek_meldings_proces(bevinding)
# Neem geautomatiseerde herstelactie
if 'UnauthorizedAccess' in bevinding_type:
isoleer_gecompromitteerde_resource(bevinding)
return {
'statusCode': 200,
'incident_id': bevinding['id'],
'ernst': ernst
}
def informeer_fg(bevinding):
"""Informeer Functionaris Gegevensbescherming"""
sns = boto3.client('sns', region_name='eu-west-1')
bericht = f"""
URGENT: Mogelijk AVG Datalek Gedetecteerd
Bevinding Type: {bevinding['type']}
Ernst: {bevinding['severity']}
Resource: {bevinding['resource']['resourceType']}
Tijd: {bevinding['createdAt']}
Actie Vereist: Beoordeel of dit een datalek is dat melding vereist
aan de Autoriteit Persoonsgegevens binnen 72 uur (AVG Artikel 33).
"""
sns.publish(
TopicArn=os.environ['FG_TOPIC_ARN'],
Subject='AVG Datalek Alert - Onmiddellijke Actie Vereist',
Message=bericht
)
10. Privacy by Design en Default
Infrastructure as Code met Privacy Controls:
import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as kms from 'aws-cdk-lib/aws-kms';
export class AVGCompliantStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Privacy by default: encryptie key
const encryptieKey = new kms.Key(this, 'StandaardEncryptieKey', {
enableKeyRotation: true,
description: 'Standaard encryptie voor alle data in rust',
});
// Privacy by default: beveiligde bucket sjabloon
const maakBeveiligdeBucket = (naam: string) => {
return new s3.Bucket(this, naam, {
encryption: s3.BucketEncryption.KMS,
encryptionKey: encryptieKey,
bucketKeyEnabled: true,
versioned: true,
enforceSSL: true,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
lifecycleRules: [
{
expiration: cdk.Duration.days(365),
},
],
});
};
// Alle buckets zijn standaard beveiligd
const dataBucket = maakBeveiligdeBucket('KlantDataBucket');
// Tag voor AVG compliance
cdk.Tags.of(this).add('AVG-Compliant', 'ja');
cdk.Tags.of(this).add('DataLocatie', 'EU');
}
}
AVG Compliance Automatisering
Geautomatiseerd Compliance Dashboard
import boto3
from datetime import datetime
def genereer_avg_compliance_rapport():
"""Genereer geautomatiseerd AVG compliance rapport"""
rapport = {
'gegenereerd_op': datetime.now().isoformat(),
'controles': []
}
# Controle 1: Data residency
rapport['controles'].append({
'beheersmaatregel': 'Data Residency',
'status': controleer_data_residency(),
'vereiste': 'Alle resources in EU regio\'s'
})
# Controle 2: Encryptie in rust
rapport['controles'].append({
'beheersmaatregel': 'Encryptie in Rust',
'status': controleer_encryptie_in_rust(),
'vereiste': 'Alle S3 buckets en RDS databases versleuteld'
})
# Controle 3: Encryptie tijdens transport
rapport['controles'].append({
'beheersmaatregel': 'Encryptie tijdens Transport',
'status': controleer_encryptie_tijdens_transport(),
'vereiste': 'HTTPS/TLS geforceerd'
})
# Controle 4: Toegang logging
rapport['controles'].append({
'beheersmaatregel': 'Toegang Logging',
'status': controleer_cloudtrail_ingeschakeld(),
'vereiste': 'CloudTrail ingeschakeld in alle regio\'s'
})
# Controle 5: Data retentie
rapport['controles'].append({
'beheersmaatregel': 'Data Retentie',
'status': controleer_lifecycle_policies(),
'vereiste': 'Lifecycle policies geconfigureerd'
})
# Bereken compliance score
geslaagd = sum(1 for controle in rapport['controles'] if controle['status'] == 'COMPLIANT')
totaal = len(rapport['controles'])
rapport['compliance_score'] = f"{(geslaagd/totaal)*100:.1f}%"
return rapport
def controleer_encryptie_in_rust():
"""Controleer of alle S3 buckets encryptie hebben ingeschakeld"""
s3 = boto3.client('s3', region_name='eu-west-1')
buckets = s3.list_buckets()['Buckets']
niet_compliant = []
for bucket in buckets:
try:
encryptie = s3.get_bucket_encryption(Bucket=bucket['Name'])
except s3.exceptions.ClientError:
niet_compliant.append(bucket['Name'])
return 'COMPLIANT' if not niet_compliant else f'NIET_COMPLIANT: {len(niet_compliant)} buckets'
Nederlandse Specifieke AVG Overwegingen
Autoriteit Persoonsgegevens (AP) Meldingen
72-Uur Meldingsplicht:
def start_datalek_meldings_proces(bevinding):
"""Start proces voor datalek melding aan AP binnen 72 uur"""
dynamodb = boto3.resource('dynamodb', region_name='eu-west-1')
tabel = dynamodb.Table('DatalekMeldingen')
melding_deadline = datetime.now() + timedelta(hours=72)
tabel.put_item(Item={
'melding_id': str(uuid.uuid4()),
'bevinding_id': bevinding['id'],
'detectie_tijd': datetime.now().isoformat(),
'melding_deadline': melding_deadline.isoformat(),
'status': 'BEOORDELING',
'ernst': bevinding['severity'],
'autoriteit': 'Autoriteit Persoonsgegevens',
'melding_vereist': 'TE_BEPALEN'
})
NEN 7510 voor Zorginstellingen
Voor Nederlandse zorginstellingen die met medische data werken:
// Extra beveiligingslagen voor NEN 7510 compliance
const medischeDataBucket = new s3.Bucket(this, 'MedischeData', {
encryption: s3.BucketEncryption.KMS,
encryptionKey: dataEncryptieKey,
versioned: true,
enforceSSL: true,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
objectLockEnabled: true, // Extra bescherming voor medische data
lifecycleRules: [
{
expiration: cdk.Duration.days(5475), // 15 jaar bewaarplicht
},
],
});
cdk.Tags.of(medischeDataBucket).add('NEN7510', 'Ja');
cdk.Tags.of(medischeDataBucket).add('DataType', 'Medisch');
Conclusie
Het bereiken van AVG compliance op AWS vereist een uitgebreide aanpak die technologie, processen en governance combineert. Door deze beheersmaatregelen en automatiseringsscripts te implementeren, kunnen Nederlandse organisaties:
- Gegevensbescherming by design en default waarborgen
- Continue compliance monitoring handhaven
- Efficiënt reageren op verzoeken van betrokkenen
- Datalekken detecteren en binnen AVG-termijnen reageren
- Verantwoording afleggen aan toezichthoudende autoriteiten
Belangrijkste Punten:
- Gebruik AWS services ontworpen voor compliance (KMS, CloudTrail, Config, GuardDuty)
- Forceer EU data residency via SCPs en regio controles
- Implementeer uitgebreide encryptie voor data in rust en tijdens transport
- Automatiseer compliance monitoring en rapportage
- Bereid je voor op verzoeken van betrokkenen met geautomatiseerde workflows
- Onderhoud gedetailleerde audit trails voor 7+ jaar
Klaar om je AWS infrastructuur AVG-compliant te maken? Neem contact op met Forrict voor expert begeleiding afgestemd op Nederlandse organisaties.
Resources
- AWS AVG Center
- AWS Data Privacy FAQ
- AVG Officiële Tekst
- Autoriteit Persoonsgegevens
- AWS Artifact voor Compliance Rapporten
- NEN 7510 voor Zorginstellingen
Fons Biemans
AWS expert en consultant bij Forrict, gespecialiseerd in cloud architectuur en AWS best practices voor Nederlandse bedrijven.

