AVG voor softwareontwikkelaars | Wat je daadwerkelijk moet implementeren
Een op ontwikkelaars gerichte gids voor AVG-compliance. Behandelt de technische vereisten, datahandlingpatronen en code-level beslissingen die je moet nemen.
De AVG (GDPR) is van kracht sinds 2018, maar de meeste ontwikkelaarsgidsen richten zich nog steeds op de juridische theorie. Deze gids is anders. Hij behandelt de technische vereisten, de code die je moet schrijven en de architectuurbeslissingen die compliance praktisch maken in plaats van pijnlijk.
Als je software persoonsgegevens opslaat, verwerkt of aanraakt van mensen in de EU, geldt dit voor jou. Het maakt niet uit waar je bedrijf gevestigd is.
AVG-overzicht voor ontwikkelaars
Sla de 99 artikelen over. Dit is wat de AVG betekent voor je codebase:
- Verzamel alleen wat je nodig hebt. Sla geen data op “voor het geval dat.”
- Vertel gebruikers wat je met hun data doet. En krijg hun toestemming wanneer dat vereist is.
- Laat gebruikers hun data inzien, exporteren en verwijderen. Je hebt API-endpoints nodig hiervoor.
- Houd data veilig. Encryptie, toegangscontrole, auditlogs.
- Meld datalekken snel. Je hebt 72 uur om de autoriteiten te informeren na het ontdekken van een lek.
- Documenteer alles. Je verwerkingsactiviteiten, je beveiligingsmaatregelen, je datastromen.
Dat is de praktische samenvatting. De rest van deze gids laat zien hoe je elke vereiste implementeert.
De 7 belangrijkste technische vereisten
1. Toestemmingsbeheer
Toestemming moet vrij gegeven, specifiek, geïnformeerd en ondubbelzinnig zijn. Vooraf aangevinkte vakjes tellen niet. Gebundelde toestemming (“akkoord met alles”) telt niet. Intrekking moet net zo eenvoudig zijn als het geven van toestemming.
Wat te bouwen
Een toestemmingssysteem heeft drie componenten nodig:
- Een opslagplaats voor toestemmingsrecords. Houd voor elke gebruiker bij waarmee ze hebben ingestemd, wanneer en hoe.
- Een toestemmingscontrolemechanisme. Controleer voordat je data verwerkt voor een specifiek doel of de gebruiker actieve toestemming heeft.
- Een intrekkingsmechanisme. Laat gebruikers toestemming intrekken via je UI en stop de verwerking onmiddellijk.
Databaseschema
CREATE TABLE user_consents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id),
consent_type VARCHAR(100) NOT NULL,
granted BOOLEAN NOT NULL,
granted_at TIMESTAMPTZ,
revoked_at TIMESTAMPTZ,
ip_address INET,
user_agent TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_user_consents_lookup
ON user_consents (user_id, consent_type, granted);
Sla de volledige historie op. Verwijder of overschrijf nooit toestemmingsrecords. Wanneer een gebruiker toestemming intrekt, voeg een nieuwe rij in met granted = false en stel revoked_at in. Dit geeft je een audittrail.
Toestemmingscontrole middleware
Hier is een Express-middleware die toestemming controleert voordat een verzoek wordt verwerkt:
import { Request, Response, NextFunction } from "express";
import { db } from "./database";
interface ConsentRequirement {
type: string;
required: boolean;
}
function requireConsent(consentType: string) {
return async (req: Request, res: Response, next: NextFunction) => {
const userId = req.user?.id;
if (!userId) {
return res.status(401).json({ error: "Authentication required" });
}
const consent = await db.query(
`SELECT granted FROM user_consents
WHERE user_id = $1 AND consent_type = $2
ORDER BY created_at DESC
LIMIT 1`,
[userId, consentType]
);
if (!consent.rows[0]?.granted) {
return res.status(403).json({
error: "Consent required",
consentType,
message: `You must grant "${consentType}" consent to use this feature.`,
consentUrl: `/settings/privacy`,
});
}
next();
};
}
// Usage
app.post(
"/api/newsletter/subscribe",
requireConsent("marketing_emails"),
subscribeHandler
);
app.post(
"/api/analytics/track",
requireConsent("usage_analytics"),
trackHandler
);
2. Datatoegang en export (recht op inzage)
Gebruikers hebben het recht om een kopie van alle persoonsgegevens op te vragen die je over hen bewaart. Je moet deze verstrekken in een gangbaar, machineleesbaar formaat. JSON of CSV werkt prima.
Wat te bouwen
Een endpoint dat alle persoonsgegevens voor een gebruiker verzamelt uit elke tabel en service, en het verpakt in een downloadbaar bestand.
interface DataExport {
exportedAt: string;
user: {
profile: Record<string, unknown>;
activity: Record<string, unknown>[];
consents: Record<string, unknown>[];
communications: Record<string, unknown>[];
};
}
app.get("/api/me/data-export", authenticate, async (req, res) => {
const userId = req.user.id;
const [profile, activity, consents, communications] = await Promise.all([
db.query("SELECT id, email, name, created_at FROM users WHERE id = $1", [
userId,
]),
db.query(
"SELECT action, metadata, created_at FROM user_activity WHERE user_id = $1 ORDER BY created_at DESC",
[userId]
),
db.query(
"SELECT consent_type, granted, granted_at, revoked_at FROM user_consents WHERE user_id = $1 ORDER BY created_at DESC",
[userId]
),
db.query(
"SELECT type, sent_at, subject FROM communications WHERE user_id = $1 ORDER BY sent_at DESC",
[userId]
),
]);
const exportData: DataExport = {
exportedAt: new Date().toISOString(),
user: {
profile: profile.rows[0],
activity: activity.rows,
consents: consents.rows,
communications: communications.rows,
},
};
res.setHeader("Content-Type", "application/json");
res.setHeader(
"Content-Disposition",
`attachment; filename="data-export-${userId}.json"`
);
res.json(exportData);
});
Dit endpoint moet elke tabel dekken die gebruikersdata bevat. Audit je schema grondig. Een ontbrekende tabel betekent een onvolledige export, wat een compliance-fout is.
3. Recht op verwijdering (recht om vergeten te worden)
Gebruikers kunnen verzoeken dat je al hun persoonsgegevens verwijdert. Je moet hieraan voldoen, tenzij je een wettelijke verplichting hebt om de data te bewaren (zoals belastinggegevens of fraudepreventie).
Wat te bouwen
Een verwijderingsendpoint dat gebruikersdata verwijdert of anonimiseert over alle tabellen. Dit is moeilijker dan het klinkt vanwege foreign key-beperkingen en data waar andere systemen van afhankelijk zijn.
app.delete("/api/me/account", authenticate, async (req, res) => {
const userId = req.user.id;
const client = await db.getClient();
try {
await client.query("BEGIN");
// Anonimiseer data die bewaard moet worden voor bedrijfsadministratie
await client.query(
`UPDATE orders
SET customer_name = 'deleted', customer_email = 'deleted'
WHERE user_id = $1`,
[userId]
);
// Verwijder data die volledig verwijderd kan worden
await client.query("DELETE FROM user_activity WHERE user_id = $1", [
userId,
]);
await client.query("DELETE FROM user_consents WHERE user_id = $1", [
userId,
]);
await client.query("DELETE FROM communications WHERE user_id = $1", [
userId,
]);
await client.query("DELETE FROM sessions WHERE user_id = $1", [userId]);
// Anonimiseer het gebruikersrecord in plaats van te verwijderen
// Dit behoudt referentiële integriteit
await client.query(
`UPDATE users SET
email = 'deleted-' || id || '@removed.invalid',
name = 'Deleted User',
phone = NULL,
address = NULL,
deleted_at = NOW()
WHERE id = $1`,
[userId]
);
await client.query("COMMIT");
// Activeer verwijdering in externe systemen
await Promise.allSettled([
emailService.deleteSubscriber(userId),
analyticsService.deleteUser(userId),
searchIndex.removeUser(userId),
]);
res.json({ message: "Account and personal data deleted" });
} catch (error) {
await client.query("ROLLBACK");
throw error;
} finally {
client.release();
}
});
Belangrijke beslissingen:
- Verwijderen vs. anonimiseren. Records die nodig zijn voor boekhouding (bestellingen, facturen) moeten worden geanonimiseerd. Verwijder al het andere.
- Externe systemen. Data die naar diensten van derden is verzonden, moet daar ook worden verwijderd.
- Timing. De AVG zegt “zonder onredelijke vertraging.” Voltooi verwijdering binnen 30 dagen. Voor de meeste systemen, maak het direct.
4. Dataminimalisatie
Verzamel en bewaar alleen de data die je daadwerkelijk nodig hebt voor een vastgesteld doel. Als je om een telefoonnummer vraagt maar gebruikers nooit belt, zou je het niet moeten verzamelen.
Praktische regels
- Audit elk formulierveld. Vraag voor elk veld: “Welke specifieke feature breekt als we dit verwijderen?” Als het antwoord niets is, verwijder het.
- Stel bewaartermijnen in. Bewaar data niet voor altijd. Definieer hoe lang elk type data nodig is en verwijder het automatisch.
- Minimaliseer logging. Strip persoonsgegevens uit logregels. Log gebruikers-ID’s, geen namen of e-mailadressen.
-- Automatisch databewaarbeleid met PostgreSQL
-- Draai dit als een geplande taak (bijv. pg_cron)
DELETE FROM user_activity
WHERE created_at < NOW() - INTERVAL '2 years';
DELETE FROM session_logs
WHERE created_at < NOW() - INTERVAL '90 days';
DELETE FROM password_reset_tokens
WHERE created_at < NOW() - INTERVAL '24 hours';
5. Encryptie
De AVG vereist “passende technische maatregelen” om persoonsgegevens te beschermen. Encryptie is de belangrijkste.
At rest
- Versleutel je databaseschijf. Alle grote cloudproviders ondersteunen dit. Schakel het in en verifieer.
- Voeg voor zeer gevoelige velden (BSN-nummers, gezondheidsdata) applicatieniveau-encryptie toe bovenop.
- Versleutel back-ups. Een onversleutelde back-up is een datalek dat wacht om te gebeuren.
In transit
- TLS overal. Elke verbinding tussen services, databases en gebruikers. Geen uitzonderingen.
- Forceer HTTPS. Redirect HTTP. Stel HSTS-headers in.
- Gebruik TLS voor databaseverbindingen. PostgreSQL ondersteunt dit native.
// PostgreSQL-verbinding met TLS
import { Pool } from "pg";
const pool = new Pool({
host: process.env.DB_HOST,
port: 5432,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
ssl: {
rejectUnauthorized: true,
ca: fs.readFileSync("/path/to/server-ca.pem").toString(),
},
});
6. Melding van datalekken
Als persoonsgegevens gecompromitteerd zijn, moet je de relevante gegevensbeschermingsautoriteit binnen 72 uur op de hoogte stellen. Als het lek een hoog risico vormt voor individuen, moet je ook de getroffen gebruikers informeren.
Wat te bouwen
- Auditlogging. Houd elke toegang tot persoonsgegevens bij. Wie heeft het benaderd, wanneer en van waar.
- Anomaliedetectie. Waarschuw bij ongebruikelijke toegangspatronen (bulk data-exports, toegang van nieuwe IP-adressen, toegang buiten kantooruren).
- Een incidentresponsplan. Documenteer wie wat doet wanneer een lek wordt ontdekt. Dit is geen code. Het is een checklist die je team oefent.
CREATE TABLE data_access_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL,
accessed_by UUID NOT NULL,
access_type VARCHAR(50) NOT NULL,
resource_type VARCHAR(100) NOT NULL,
resource_id UUID,
ip_address INET,
user_agent TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_data_access_log_user
ON data_access_log (user_id, created_at);
CREATE INDEX idx_data_access_log_accessor
ON data_access_log (accessed_by, created_at);
7. Privacy by design
De AVG zegt dat privacy vanaf het begin in systemen moet worden ingebouwd, niet er achteraf aan vastgeplakt. In de praktijk betekent dit privacy als standaard.
- Standaard privé. Nieuwe features moeten minimale data verzamelen en opt-in vereisen voor alles buiten de kernfunctie.
- Instellingen staan standaard op de meest privacyvriendelijke optie. Gebruikers die nooit hun instellingen aanraken, moeten de hoogste privacybescherming hebben.
- Scheid belangen. Meng geen analyticsdata met functionele data. Hergebruik geen auth-tokens voor tracking.
Anonimisering vs. pseudonimisering
Dit zijn niet hetzelfde, en het onderscheid is belangrijk.
- Pseudonimisering vervangt identificerende informatie door een omkeerbaar token. Voorbeeld: e-mailadressen hashen. Als je de hashfunctie en het originele e-mailadres hebt, kun je de persoon opnieuw identificeren. De AVG is nog steeds van toepassing omdat heridentificatie mogelijk is.
- Anonimisering verwijdert identificerende informatie permanent. Voorbeeld: geaggregeerde analytics (“1.247 gebruikers bezochten de prijspagina”) zonder mogelijkheid om te identificeren welke gebruikers. De AVG is niet van toepassing op echt geanonimiseerde data.
Echte anonimisering is moeilijk. Als je “anonieme” dataset een tijdstempel, een stad en een apparaattype bevat, kan die combinatie iemand uniek identificeren. Wees conservatief.
Cookietoestemming
Als je website cookies gebruikt die verder gaan dan wat strikt noodzakelijk is, heb je toestemming nodig voordat je ze plaatst.
Vereist toestemming: analytische cookies, advertentiepixels, social media-widgets, elk trackingscript van derden.
Vereist geen toestemming: sessiecookies, winkelwagencookies, CSRF-tokens, de cookie voor cookietoestemmingsvoorkeur zelf.
Je toestemmingsbanner moet niet-essentiële cookies blokkeren totdat toestemming is gegeven, gedetailleerde keuzes bieden en “alles weigeren” net zo eenvoudig maken als “alles accepteren.” Bouw dit niet vanaf nul. Tools zoals Cookiebot verwerken de complexiteit. De kernregel: geen trackingscripts draaien voordat toestemming is gegeven.
Derde partijen als dataverwerkers
Elke dienst van derden die de data van je gebruikers verwerkt, is een “verwerker” onder de AVG. Jij bent verantwoordelijk voor hun compliance.
Wat te controleren
Controleer voordat je een dienst van derden integreert die persoonsgegevens aanraakt:
- Hebben ze een verwerkersovereenkomst? Een verwerkersovereenkomst is verplicht. De meeste SaaS-providers publiceren die openbaar.
- Waar slaan ze data op? Als buiten de EU, verifieer de juridische basis voor de doorgifte.
- Welke data benaderen ze? Minimaliseer wat je verstuurt. Als de dienst alleen een e-mailadres nodig heeft, stuur niet het volledige profiel.
- Kun je data uit hun systemen laten verwijderen? Verwijderingsverzoeken van gebruikers moeten overal worden doorgevoerd.
- Hoe gaan ze om met datalekken? Hun verwerkersovereenkomst moet meldingstermijnen specificeren.
Veelvoorkomende derden om te beoordelen
- E-maildiensten (Resend, SendGrid, Mailchimp)
- Analytics (Google Analytics, Mixpanel, Amplitude)
- Foutopsporing (Sentry, Bugsnag)
- Betalingsverwerking (Stripe, Adyen)
- Cloudhosting (AWS, Google Cloud, Vercel)
- Klantenservicetools (Intercom, Zendesk)
- AI API’s (OpenAI, Anthropic, Google AI)
Onderhoud een lijst van alle verwerkers. Beoordeel deze elk kwartaal.
Bewaarbeleid voor data
Bewaar persoonsgegevens niet langer dan noodzakelijk. Definieer bewaartermijnen voor elk datatype.
| Datatype | Aanbevolen bewaartermijn | Reden |
|---|---|---|
| Gebruikersaccountdata | Tot verwijdering wordt gevraagd | Nodig voor de dienst |
| Sessielogs | 90 dagen | Beveiliging en debugging |
| Gebruikersactiviteitenlogs | 1-2 jaar | Productanalytics |
| Supporttickets | 3 jaar | Servicekwaliteit |
| Financiële records | 7 jaar | Fiscale/wettelijke verplichtingen |
| Wachtwoord-resettokens | 24 uur | Beveiliging |
| Mislukte inlogpogingen | 90 dagen | Beveiligingsmonitoring |
Implementeer geautomatiseerde opschoontaken. Vertrouw niet op iemand die eraan denkt een script te draaien.
AVG-checklist voor ontwikkelaars
Gebruik dit als startpunt bij het bouwen of auditen van een systeem.
Dataverzameling
- Elk formulierveld heeft een vastgesteld doel
- Er worden geen onnodige data verzameld
- Het privacybeleid is gelinkt vanuit elk dataverzamelpunt
- Toestemming wordt verzameld voordat verwerking plaatsvindt (waar vereist)
- Toestemmingsrecords worden opgeslagen met tijdstempels
Dataopslag
- Database-encryptie at rest is ingeschakeld
- TLS is afgedwongen voor alle verbindingen
- Gevoelige velden hebben applicatieniveau-encryptie
- Back-ups zijn versleuteld
- Toegang tot productiedata is beperkt en gelogd
Gebruikersrechten
- Data-exportendpoint bestaat en dekt alle tabellen
- Accountverwijderingsendpoint bestaat en verwerkt alle data
- Gebruikers kunnen toestemming bekijken en intrekken in hun instellingen
- Verwijdering wordt doorgevoerd naar diensten van derden
- Alle verzoeken inzake gebruikersrechten worden binnen 30 dagen beantwoord
Cookies en tracking
- Cookietoestemmingsbanner is geïmplementeerd
- Niet-essentiële cookies worden geblokkeerd vóór toestemming
- Toestemmingskeuzes zijn gedetailleerd (niet alles-of-niets)
- “Alles weigeren” is even prominent als “Alles accepteren”
Derde partijen
- Alle dataverwerkers zijn gedocumenteerd
- Verwerkersovereenkomsten zijn getekend met elke verwerker
- Data die naar derden wordt verzonden is geminimaliseerd
- Dataverwijdering bij derden is mogelijk
Beveiliging
- Auditlogs volgen toegang tot persoonsgegevens
- Anomaliewaarschuwingen zijn geconfigureerd
- Incidentresponsplan is gedocumenteerd
- Datalekmeldingenproces is gedefinieerd (72-uursdeadline)
Bewaring
- Bewaartermijnen zijn gedefinieerd voor alle datatypen
- Geautomatiseerde opschoontaken zijn ingepland
- Verlopen data wordt daadwerkelijk verwijderd (verifieer dit)
Slotgedachten
AVG-compliance is geen eenmalig project. Het is een set praktijken verweven in hoe je software bouwt. Het technische werk is rechtlijnig: toestemmingsopslag, data-export, verwijderingsendpoints, encryptie, auditlogging. Standaard engineering.
Het moeilijke deel is grondig zijn. Het is makkelijk om dat logbestand, die analytics-event of die integratie van derden die gebruikers-e-mails opslaat te vergeten. Audit regelmatig. Test je verwijderingsendpoint. Verifieer dat je exports compleet zijn. Bouw privacy in je proces in vanaf het begin.
Hulp nodig bij het bouwen van AVG-conforme software of het auditen van je bestaande systemen? Neem contact op. We bouwen privacy-first applicaties voor Europese bedrijven en bedrijven die EU-gebruikers bedienen.