GDPR programinės įrangos kūrėjams | Ką iš tikrųjų reikia įgyvendinti
Kūrėjams skirtas GDPR atitikties vadovas. Apima techninius reikalavimus, duomenų tvarkymo modelius ir kodo lygio sprendimus, kuriuos reikia priimti.
GDPR galioja nuo 2018 m., bet dauguma kūrėjų vadovų vis dar orientuojasi į teisinę teoriją. Šis vadovas kitoks. Jis apima techninius reikalavimus, kodą, kurį reikia parašyti, ir architektūrinius sprendimus, kurie atitiktį padaro praktišką, o ne skausmingą.
Jei jūsų programinė įranga saugo, apdoroja ar liečia ES žmonių asmens duomenis, tai taikoma jums. Nesvarbu, kur yra jūsų įmonė.
GDPR apžvalga kūrėjams
Praleiskite 99 straipsnius. Štai ką GDPR reiškia jūsų kodui:
- Rinkite tik tai, ko reikia. Nesaugokite duomenų „visam atvejui”.
- Pasakykite vartotojams, ką darote su jų duomenimis. Ir gaukite jų leidimą, kai reikia.
- Leiskite vartotojams pasiekti, eksportuoti ir ištrinti savo duomenis. Tam reikia API galutinių taškų.
- Saugokite duomenis. Šifravimas, prieigos kontrolė, audito žurnalai.
- Praneškite apie pažeidimus greitai. Turite 72 valandas pranešti valdžios institucijoms po pažeidimo aptikimo.
- Dokumentuokite viską. Savo apdorojimo veiklas, saugumo priemones, duomenų srautus.
Tai praktinė santrauka. Likusi vadovo dalis rodo, kaip kiekvieną reikalavimą įgyvendinti.
7 pagrindiniai techniniai reikalavimai
1. Sutikimų valdymas
Sutikimas turi būti laisva valia duotas, konkretus, informuotas ir nedviprasmiškas. Iš anksto pažymėti laukeliai netinka. Sujungtas sutikimas („sutikite su viskuo”) netinka. Atšaukimas turi būti toks pat paprastas kaip sutikimo davimas.
Ką kurti
Sutikimų sistema reikalauja trijų komponentų:
- Sutikimų įrašų saugykla. Kiekvienam vartotojui sekite, su kuo sutiko, kada ir kaip.
- Sutikimo tikrinimo mechanizmas. Prieš apdorodami duomenis konkrečiam tikslui, patikrinkite, ar vartotojas turi aktyvų sutikimą.
- Atšaukimo mechanizmas. Leiskite vartotojams atšaukti sutikimą per jūsų sąsają ir nedelsdami sustabdykite apdorojimą.
Duomenų bazės schema
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);
Saugokite visą istoriją. Niekada neištrinkite ir neperrašykite sutikimų įrašų. Kai vartotojas atšaukia sutikimą, įterpkite naują eilutę su granted = false ir nustatykite revoked_at. Tai suteikia audito pėdsaką.
Sutikimo tikrinimo tarpinė programinė įranga
Štai Express tarpinė programinė įranga, tikrinanti sutikimą prieš apdorodama užklausą:
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. Duomenų prieiga ir eksportavimas (teisė į prieigą)
Vartotojai turi teisę prašyti visų jų asmens duomenų kopijos. Turite ją pateikti plačiai naudojamu, mašininiu būdu skaitomu formatu. JSON arba CSV tinka.
Ką kurti
Galutinį tašką, kuris surenka visus asmens duomenis vartotojui iš kiekvienos lentelės ir paslaugos, tada supakuoja į atsisiunčiamą failą.
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);
});
Šis galutinis taškas turi apimti kiekvieną lentelę, kurioje yra vartotojo duomenų. Kruopščiai audituokite savo schemą. Trūkstama lentelė reiškia neišsamų eksportą, o tai yra atitikties pažeidimas.
3. Teisė į ištrynimą (teisė būti pamirštam)
Vartotojai gali prašyti ištrinti visus jų asmens duomenis. Turite vykdyti, nebent turite teisinę prievolę juos saugoti (kaip mokesčių įrašus ar sukčiavimo prevenciją).
Ką kurti
Ištrynimo galutinį tašką, kuris pašalina arba anonimizuoja vartotojo duomenis visose lentelėse. Tai sudėtingiau nei skamba dėl išorinių raktų apribojimų ir duomenų, nuo kurių priklauso kitos sistemos.
app.delete("/api/me/account", authenticate, async (req, res) => {
const userId = req.user.id;
const client = await db.getClient();
try {
await client.query("BEGIN");
// Anonymize data that must be retained for business records
await client.query(
`UPDATE orders
SET customer_name = 'deleted', customer_email = 'deleted'
WHERE user_id = $1`,
[userId]
);
// Delete data that can be fully removed
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]);
// Anonymize the user record instead of deleting
// This preserves referential integrity
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");
// Trigger deletion in external systems
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();
}
});
Pagrindiniai sprendimai:
- Trinti ar anonimizuoti. Įrašai, reikalingi apskaitai (užsakymai, sąskaitos), turėtų būti anonimizuoti. Visa kita trinkite.
- Išorinės sistemos. Trečiųjų šalių paslaugoms išsiųsti duomenys turi būti ir ten ištrinti.
- Terminas. GDPR sako „be nepagrįsto delsimo”. Užbaikite ištrynimą per 30 dienų. Daugumai sistemų darykite tai nedelsiant.
4. Duomenų minimizavimas
Rinkite ir saugokite tik tuos duomenis, kurių tikrai reikia nurodytam tikslui. Jei prašote telefono numerio, bet niekada neskambinate vartotojams, neturėtumėte jo rinkti.
Praktinės taisyklės
- Audituokite kiekvieną formos lauką. Kiekvienam laukui klauskite: „Kokia konkreti funkcija nustos veikti, jei tai pašalinsime?” Jei atsakymas niekas, pašalinkite.
- Nustatykite saugojimo terminus. Nesaugokite duomenų amžinai. Apibrėžkite, kiek laiko reikia kiekvieno tipo duomenų, tada automatiškai juos trinkite.
- Minimizuokite žurnalus. Pašalinkite asmens duomenis iš žurnalo įrašų. Žurnalizuokite vartotojų ID, ne vardus ar el. pašto adresus.
-- Automatic data retention with PostgreSQL
-- Run this as a scheduled job (e.g., 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. Šifravimas
GDPR reikalauja „tinkamų techninių priemonių” asmens duomenims apsaugoti. Šifravimas yra svarbiausias.
Ramybės būsenoje
- Šifruokite duomenų bazės diską. Visi pagrindiniai debesų tiekėjai tai palaiko. Įjunkite ir patikrinkite.
- Labai jautriems laukams (asmens kodai, sveikatos duomenys) pridėkite programos lygio šifravimą papildomai.
- Šifruokite atsargines kopijas. Nešifruota atsarginė kopija yra pažeidimas, laukiantis savo valandos.
Perduodant
- TLS visur. Kiekvienas ryšys tarp paslaugų, duomenų bazių ir vartotojų. Jokių išimčių.
- Priverskite HTTPS. Nukreipkite HTTP. Nustatykite HSTS antraštes.
- Naudokite TLS duomenų bazės ryšiams. PostgreSQL tai palaiko natūraliai.
// PostgreSQL connection with 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. Pranešimas apie pažeidimus
Jei asmens duomenys pažeidžiami, turite pranešti atitinkamai Duomenų apsaugos institucijai per 72 valandas. Jei pažeidimas kelia didelę riziką asmenims, turite pranešti ir paveiktiems vartotojams.
Ką kurti
- Audito žurnalai. Sekite kiekvieną prieigą prie asmens duomenų. Kas priėjo, kada ir iš kur.
- Anomalijų aptikimas. Signalizuokite apie neįprastus prieigos modelius (masinis duomenų eksportavimas, prieiga iš naujų IP, prieiga ne darbo valandomis).
- Incidentų reagavimo planas. Dokumentuokite, kas ką daro, kai aptinkamas pažeidimas. Tai ne kodas. Tai kontrolinis sąrašas, kurį jūsų komanda praktikuoja.
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. Privatumas pagal dizainą
GDPR sako, kad privatumas turėtų būti įmontuotas į sistemas nuo pat pradžių, o ne pridėtas vėliau. Praktikoje tai reiškia, kad privatumas yra numatytas.
- Numatytas privatumas. Naujos funkcijos turėtų rinkti minimaliai duomenų ir reikalauti dalyvavimo viskam, kas viršija pagrindinę funkciją.
- Nustatymai numatyti kaip privačiausi. Vartotojai, kurie niekada neliečia savo nustatymų, turėtų turėti aukščiausią privatumo apsaugą.
- Atskirkite rūpesčius. Nemaišykite analitikos duomenų su funkciniais duomenimis. Naudokite autentifikacijos žetonų pakartotinai sekimui.
Anonimizavimas vs. pseudonimizavimas
Tai nėra tas pats, ir skirtumas svarbus.
- Pseudonimizavimas pakeičia identifikuojančią informaciją grįžtamu žetonu. Pavyzdys: el. pašto adresų maišos funkcija. Jei turite maišos funkciją ir originalų el. paštą, galite asmens tapatybę atkurti. GDPR vis tiek taikomas, nes pakartotinis identifikavimas įmanomas.
- Anonimizavimas pašalina identifikuojančią informaciją negrįžtamai. Pavyzdys: agreguota analitika („1 247 vartotojai aplankė kainodaros puslapį”) be galimybės nustatyti, kurie vartotojai. GDPR netaikomas tikrai anonimizuotiems duomenims.
Tikras anonimizavimas yra sunkus. Jei jūsų „anoniminis” duomenų rinkinys apima laiko žymą, miestą ir įrenginio tipą, ta kombinacija gali unikaliai identifikuoti asmenį. Būkite konservatyvūs.
Slapukų sutikimas
Jei jūsų svetainė naudoja slapukus, viršijančius tai, kas griežtai būtina, reikia sutikimo prieš juos nustatant.
Reikia sutikimo: analitikos slapukai, reklamos pikseliai, socialinių tinklų valdikliai, bet kokie trečiųjų šalių sekimo scenarijai.
Nereikia sutikimo: sesijos slapukai, pirkinių krepšelio slapukai, CSRF žetonai, pats slapukų sutikimo pageidavimų slapukas.
Jūsų sutikimo juosta turėtų blokuoti nebūtinus slapukus, kol sutikimas duotas, siūlyti detalius pasirinkimus ir padaryti „atmesti visus” taip pat lengvą kaip „priimti visus”. Nekurkite to nuo nulio. Įrankiai kaip Cookiebot tvarko sudėtingumą. Pagrindinė taisyklė: jokie sekimo scenarijai neveikia prieš duodant sutikimą.
Trečiųjų šalių duomenų tvarkytojai
Kiekviena trečiosios šalies paslauga, tvarkanti jūsų vartotojų duomenis, yra „duomenų tvarkytojas” pagal GDPR. Jūs atsakote už jų atitiktį.
Ką tikrinti
Prieš integruodami bet kurią trečiosios šalies paslaugą, liečiančią asmens duomenis:
- Ar jie turi DPA? Duomenų tvarkymo sutartis yra privaloma. Dauguma SaaS tiekėjų jas skelbia viešai.
- Kur jie saugo duomenis? Jei ne ES, patikrinkite teisinį pagrindą perdavimui.
- Kokius duomenis jie pasiekia? Minimizuokite tai, ką siunčiate. Jei paslaugai reikia tik el. pašto, nesiųskite viso profilio.
- Ar galite ištrinti duomenis iš jų sistemų? Vartotojų trynimo užklausos turi pasiekti visur.
- Kaip jie tvarko pažeidimus? Jų DPA turėtų nurodyti pranešimo terminus.
Dažni trečiųjų šalių tvarkytojai peržiūrai
- El. pašto paslaugos (Resend, SendGrid, Mailchimp)
- Analitika (Google Analytics, Mixpanel, Amplitude)
- Klaidų sekimas (Sentry, Bugsnag)
- Mokėjimų apdorojimas (Stripe, Adyen)
- Debesų priegloba (AWS, Google Cloud, Vercel)
- Klientų palaikymo įrankiai (Intercom, Zendesk)
- AI API (OpenAI, Anthropic, Google AI)
Sudarykite visų tvarkytojų sąrašą. Peržiūrėkite kas ketvirtį.
Duomenų saugojimo politikos
Nesaugokite asmens duomenų ilgiau nei būtina. Apibrėžkite saugojimo terminus kiekvienam duomenų tipui.
| Duomenų tipas | Siūlomas saugojimas | Priežastis |
|---|---|---|
| Vartotojo paskyros duomenys | Kol prašoma ištrinti | Reikalingi paslaugai |
| Sesijos žurnalai | 90 dienų | Saugumas ir derinimas |
| Vartotojo veiklos žurnalai | 1-2 metai | Produkto analitika |
| Palaikymo užklausos | 3 metai | Paslaugos kokybė |
| Finansiniai įrašai | 7 metai | Mokesčių/teisinės prievolės |
| Slaptažodžio atstatymo žetonai | 24 valandos | Saugumas |
| Nepavykę prisijungimo bandymai | 90 dienų | Saugumo stebėjimas |
Įgyvendinkite automatizuotas valymo užduotis. Nepasitikėkite tuo, kad kas nors prisimins paleisti scenarijų.
GDPR kontrolinis sąrašas kūrėjams
Naudokite kaip pradžios tašką kuriant ar audituojant sistemą.
Duomenų rinkimas
- Kiekvienas formos laukas turi nurodytą tikslą
- Nereikalingi duomenys nerenkami
- Privatumo politika susieta su kiekvienu duomenų rinkimo tašku
- Sutikimas surenkamas prieš apdorojimą (kai reikalaujama)
- Sutikimo įrašai saugomi su laiko žymomis
Duomenų saugojimas
- Duomenų bazės šifravimas ramybės būsenoje įjungtas
- TLS priverstinis visiems ryšiams
- Jautrūs laukai turi programos lygio šifravimą
- Atsarginės kopijos šifruotos
- Prieiga prie gamybos duomenų ribota ir registruojama
Vartotojų teisės
- Duomenų eksporto galutinis taškas egzistuoja ir apima visas lenteles
- Paskyros trynimo galutinis taškas egzistuoja ir apdoroja visus duomenis
- Vartotojai gali peržiūrėti ir atšaukti sutikimą nustatymuose
- Trynimas perduodamas trečiųjų šalių paslaugoms
- Visi vartotojų teisių prašymai atsakomi per 30 dienų
Slapukai ir sekimas
- Slapukų sutikimo juosta įgyvendinta
- Nebūtini slapukai blokuojami prieš sutikimą
- Sutikimo pasirinkimai detalūs (ne viskas arba nieko)
- „Atmesti visus” yra taip pat matomas kaip „Priimti visus”
Trečiosios šalys
- Visi duomenų tvarkytojai dokumentuoti
- DPA pasirašyti su kiekvienu tvarkytoju
- Trečiosioms šalims siunčiami duomenys minimizuoti
- Duomenų trynimas iš trečiųjų šalių įmanomas
Saugumas
- Audito žurnalai seka prieigą prie asmens duomenų
- Anomalijų signalizavimas sukonfigūruotas
- Incidentų reagavimo planas dokumentuotas
- Pranešimo apie pažeidimus procesas apibrėžtas (72 valandų terminas)
Saugojimas
- Saugojimo terminai apibrėžti visiems duomenų tipams
- Automatizuotos valymo užduotys suplanuotos
- Pasibaigę duomenys tikrai ištrinami (tai patikrinkite)
Galutinės mintys
GDPR atitiktis nėra vienkartinis projektas. Tai praktikų rinkinys, įpintas į programinės įrangos kūrimo procesą. Techninis darbas paprastas: sutikimų saugojimas, duomenų eksportavimas, trynimo galutiniai taškai, šifravimas, audito registravimas. Standartinė inžinerija.
Sunkioji dalis yra kruopštumas. Lengva pamiršti tą žurnalo failą, tą analitikos įvykį ar tą trečiosios šalies integraciją, saugančią vartotojų el. paštus. Reguliariai audituokite. Testuokite savo trynimo galutinį tašką. Patikrinkite, ar eksportai yra pilni. Įtraukite privatumą į savo procesą nuo pat pradžių.
Reikia pagalbos kuriant GDPR atitinkančią programinę įrangą ar audituojant esamas sistemas? Susisiekite. Kuriame privatumą pirmenybę teikiančias programas Europos verslui ir įmonėms, aptarnaujančioms ES vartotojus.