GDPR dla programistów | Co naprawdę musisz zaimplementować
Przewodnik po zgodności z GDPR skierowany do programistów. Obejmuje wymagania techniczne, wzorce obsługi danych i decyzje na poziomie kodu.
GDPR obowiązuje od 2018 roku, ale większość przewodników dla programistów wciąż skupia się na teorii prawnej. Ten przewodnik jest inny. Obejmuje wymagania techniczne, kod, który musisz napisać, i decyzje architektoniczne, które sprawiają, że zgodność jest praktyczna, a nie bolesna.
Jeśli Twoje oprogramowanie przechowuje, przetwarza lub dotyka danych osobowych osób w UE, to Cię dotyczy. Nie ma znaczenia, gdzie Twoja firma ma siedzibę.
Przegląd GDPR dla programistów
Pomiń 99 artykułów. Oto co GDPR oznacza dla Twojego kodu:
- Zbieraj tylko to, czego potrzebujesz. Nie przechowuj danych “na wszelki wypadek.”
- Informuj użytkowników, co robisz z ich danymi. I uzyskaj ich zgodę, gdy jest wymagana.
- Pozwól użytkownikom uzyskać dostęp, wyeksportować i usunąć ich dane. Potrzebujesz endpointów API do tego.
- Dbaj o bezpieczeństwo danych. Szyfrowanie, kontrola dostępu, logi audytowe.
- Zgłaszaj naruszenia szybko. Masz 72 godziny na powiadomienie organów po wykryciu naruszenia.
- Dokumentuj wszystko. Twoje czynności przetwarzania, środki bezpieczeństwa, przepływy danych.
To praktyczne podsumowanie. Reszta tego przewodnika pokazuje, jak zaimplementować każde wymaganie.
7 kluczowych wymagań technicznych
1. Zarządzanie zgodami
Zgoda musi być dobrowolna, konkretna, świadoma i jednoznaczna. Zaznaczone z góry pola wyboru się nie liczą. Zgoda zbiorcza (“zgadzam się na wszystko”) się nie liczy. Wycofanie musi być tak łatwe jak udzielenie zgody.
Co zbudować
System zgód potrzebuje trzech komponentów:
- Magazyn rekordów zgód. Dla każdego użytkownika śledź, na co wyraził zgodę, kiedy i jak.
- Mechanizm sprawdzania zgody. Przed przetworzeniem danych w określonym celu sprawdź, czy użytkownik ma aktywną zgodę.
- Mechanizm wycofania. Pozwól użytkownikom wycofać zgodę przez interfejs i natychmiast zaprzestań przetwarzania.
Schemat bazy danych
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);
Przechowuj pełną historię. Nigdy nie usuwaj ani nie nadpisuj rekordów zgód. Gdy użytkownik wycofuje zgodę, wstaw nowy wiersz z granted = false i ustaw revoked_at. To daje Ci ścieżkę audytu.
Middleware sprawdzania zgody
Oto middleware Express, który sprawdza zgodę przed przetworzeniem żądania:
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. Dostęp do danych i eksport (prawo dostępu)
Użytkownicy mają prawo zażądać kopii wszystkich danych osobowych, które o nich przechowujesz. Musisz je dostarczyć w powszechnie używanym, nadającym się do odczytu maszynowego formacie. JSON lub CSV wystarczy.
Co zbudować
Endpoint, który zbiera wszystkie dane osobowe użytkownika ze wszystkich tabel i usług, a następnie pakuje je w plik do pobrania.
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);
});
Ten endpoint musi obejmować każdą tabelę zawierającą dane użytkownika. Dokładnie przeanalizuj swój schemat. Brakująca tabela oznacza niekompletny eksport, co jest naruszeniem zgodności.
3. Prawo do usunięcia (prawo do bycia zapomnianym)
Użytkownicy mogą zażądać usunięcia wszystkich swoich danych osobowych. Musisz się zastosować, chyba że masz prawny obowiązek ich zachowania (jak dokumentacja podatkowa lub zapobieganie oszustwom).
Co zbudować
Endpoint usuwania, który kasuje lub anonimizuje dane użytkownika we wszystkich tabelach. To trudniejsze niż się wydaje z powodu ograniczeń kluczy obcych i danych, od których zależą inne systemy.
app.delete("/api/me/account", authenticate, async (req, res) => {
const userId = req.user.id;
const client = await db.getClient();
try {
await client.query("BEGIN");
// Anonimizuj dane, które muszą być zachowane dla dokumentacji biznesowej
await client.query(
`UPDATE orders
SET customer_name = 'deleted', customer_email = 'deleted'
WHERE user_id = $1`,
[userId]
);
// Usuń dane, które mogą być w pełni skasowane
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]);
// Anonimizuj rekord użytkownika zamiast go usuwać
// To zachowuje integralność referencyjną
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");
// Wyzwól usunięcie w systemach zewnętrznych
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();
}
});
Kluczowe decyzje:
- Usuwać vs anonimizować. Rekordy potrzebne do księgowości (zamówienia, faktury) powinny być anonimizowane. Resztę usuwaj.
- Systemy zewnętrzne. Dane wysłane do usług zewnętrznych muszą być tam również usunięte.
- Terminy. GDPR mówi “bez zbędnej zwłoki.” Zakończ usunięcie w ciągu 30 dni. W większości systemów zrób to natychmiast.
4. Minimalizacja danych
Zbieraj i przechowuj tylko dane, których faktycznie potrzebujesz do określonego celu. Jeśli pytasz o numer telefonu, ale nigdy nie dzwonisz do użytkowników, nie powinieneś go zbierać.
Praktyczne zasady
- Przeanalizuj każde pole formularza. Dla każdego pola zapytaj: “Jaka konkretna funkcja się zepsuje, jeśli to usuniemy?” Jeśli odpowiedź brzmi “żadna”, usuń je.
- Ustaw okresy przechowywania. Nie przechowuj danych wiecznie. Określ, jak długo każdy typ danych jest potrzebny, a potem automatycznie usuwaj.
- Minimalizuj logowanie. Usuwaj dane osobowe z wpisów logów. Loguj identyfikatory użytkowników, nie imiona czy e-maile.
-- Automatyczna retencja danych z PostgreSQL
-- Uruchamiaj jako zaplanowane zadanie (np. 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. Szyfrowanie
GDPR wymaga “odpowiednich środków technicznych” do ochrony danych osobowych. Szyfrowanie jest najważniejszym z nich.
W spoczynku
- Szyfruj dysk bazy danych. Wszyscy główni dostawcy chmury to wspierają. Włącz i zweryfikuj.
- Dla wysoce wrażliwych pól (numery PESEL, dane medyczne) dodaj szyfrowanie na poziomie aplikacji.
- Szyfruj kopie zapasowe. Niezaszyfrowana kopia zapasowa to naruszenie czekające na swój moment.
W transmisji
- TLS wszędzie. Każde połączenie między usługami, bazami danych i użytkownikami. Bez wyjątków.
- Wymuszaj HTTPS. Przekieruj HTTP. Ustaw nagłówki HSTS.
- Używaj TLS do połączeń z bazą danych. PostgreSQL wspiera to natywnie.
// Połączenie PostgreSQL z 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. Powiadamianie o naruszeniach
Jeśli dane osobowe zostaną naruszone, musisz powiadomić odpowiedni organ ochrony danych w ciągu 72 godzin. Jeśli naruszenie stwarza wysokie ryzyko dla osób, musisz również powiadomić dotkniętych użytkowników.
Co zbudować
- Logowanie audytowe. Śledź każdy dostęp do danych osobowych. Kto uzyskał dostęp, kiedy i skąd.
- Wykrywanie anomalii. Alerty na nietypowe wzorce dostępu (masowe eksporty danych, dostęp z nowych adresów IP, dostęp poza godzinami pracy).
- Plan reagowania na incydenty. Udokumentuj, kto co robi po wykryciu naruszenia. To nie jest kod. To lista kontrolna, którą Twój zespół ćwiczy.
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. Prywatność w projekcie
GDPR mówi, że prywatność powinna być wbudowana w systemy od początku, nie dodawana później. W praktyce oznacza to uczynienie prywatności domyślną.
- Domyślnie prywatnie. Nowe funkcje powinny zbierać minimalne dane i wymagać opt-in dla wszystkiego poza kluczową funkcją.
- Ustawienia domyślne na najbardziej prywatną opcję. Użytkownicy, którzy nigdy nie ruszą swoich ustawień, powinni mieć najwyższą ochronę prywatności.
- Separacja odpowiedzialności. Nie mieszaj danych analitycznych z danymi funkcjonalnymi. Nie używaj ponownie tokenów autoryzacyjnych do śledzenia.
Anonimizacja vs pseudonimizacja
To nie to samo, a różnica ma znaczenie.
- Pseudonimizacja zastępuje informacje identyfikujące odwracalnym tokenem. Przykład: haszowanie adresów e-mail. Jeśli masz funkcję haszującą i oryginalny e-mail, możesz ponownie zidentyfikować osobę. GDPR nadal obowiązuje, ponieważ ponowna identyfikacja jest możliwa.
- Anonimizacja trwale usuwa informacje identyfikujące. Przykład: zagregowane analizy (“1247 użytkowników odwiedziło stronę cennika”) bez możliwości identyfikacji konkretnych użytkowników. GDPR nie ma zastosowania do prawdziwie zanonimizowanych danych.
Prawdziwa anonimizacja jest trudna. Jeśli Twój “anonimowy” zbiór danych zawiera znacznik czasu, miasto i typ urządzenia, ta kombinacja może jednoznacznie identyfikować kogoś. Bądź ostrożny.
Zgoda na pliki cookie
Jeśli Twoja strona używa plików cookie poza tym, co jest ściśle konieczne, potrzebujesz zgody przed ich ustawieniem.
Wymaga zgody: pliki cookie analityczne, piksele reklamowe, widgety mediów społecznościowych, wszelkie skrypty śledzące firm trzecich.
Nie wymaga zgody: pliki cookie sesji, pliki cookie koszyka zakupowego, tokeny CSRF, sam plik cookie preferencji zgody.
Twój baner zgody powinien blokować nieistotne pliki cookie do czasu udzielenia zgody, oferować szczegółowe wybory i uczynić “odrzuć wszystkie” tak łatwym jak “zaakceptuj wszystkie.” Nie buduj tego od zera. Narzędzia jak Cookiebot radzą sobie ze złożonością. Kluczowa zasada: żadne skrypty śledzące nie uruchamiają się przed udzieleniem zgody.
Zewnętrzni przetwarzający dane
Każda usługa zewnętrzna obsługująca dane Twoich użytkowników jest “podmiotem przetwarzającym” w rozumieniu GDPR. Ty ponosisz odpowiedzialność za ich zgodność.
Co sprawdzić
Przed integracją jakiejkolwiek usługi zewnętrznej dotykającej danych osobowych:
- Czy mają DPA? Umowa o przetwarzanie danych jest obowiązkowa. Większość dostawców SaaS publikuje swoją publicznie.
- Gdzie przechowują dane? Jeśli poza UE, zweryfikuj podstawę prawną transferu.
- Do jakich danych mają dostęp? Minimalizuj to, co wysyłasz. Jeśli usługa potrzebuje tylko e-maila, nie wysyłaj pełnego profilu.
- Czy możesz usunąć dane z ich systemów? Żądania usunięcia użytkownika muszą być propagowane wszędzie.
- Jak obsługują naruszenia? Ich DPA powinno określać terminy powiadamiania.
Typowi zewnętrzni przetwarzający do przeglądu
- Usługi e-mail (Resend, SendGrid, Mailchimp)
- Analityka (Google Analytics, Mixpanel, Amplitude)
- Śledzenie błędów (Sentry, Bugsnag)
- Przetwarzanie płatności (Stripe, Adyen)
- Hosting w chmurze (AWS, Google Cloud, Vercel)
- Narzędzia obsługi klienta (Intercom, Zendesk)
- API AI (OpenAI, Anthropic, Google AI)
Prowadź listę wszystkich podmiotów przetwarzających. Przeglądaj ją kwartalnie.
Polityki retencji danych
Nie przechowuj danych osobowych dłużej niż to konieczne. Zdefiniuj okresy retencji dla każdego typu danych.
| Typ danych | Sugerowana retencja | Powód |
|---|---|---|
| Dane konta użytkownika | Do żądania usunięcia | Potrzebne do działania usługi |
| Logi sesji | 90 dni | Bezpieczeństwo i debugowanie |
| Logi aktywności użytkownika | 1-2 lata | Analityka produktu |
| Zgłoszenia supportowe | 3 lata | Jakość usługi |
| Dokumenty finansowe | 7 lat | Obowiązki podatkowe/prawne |
| Tokeny resetu hasła | 24 godziny | Bezpieczeństwo |
| Nieudane próby logowania | 90 dni | Monitoring bezpieczeństwa |
Wdróż automatyczne zadania czyszczenia. Nie polegaj na tym, że ktoś pamięta o uruchomieniu skryptu.
Lista kontrolna GDPR dla programistów
Użyj tego jako punktu wyjścia przy budowaniu lub audytowaniu systemu.
Zbieranie danych
- Każde pole formularza ma określony cel
- Nie są zbierane niepotrzebne dane
- Polityka prywatności jest podlinkowana z każdego punktu zbierania danych
- Zgoda jest zbierana przed przetwarzaniem (gdy wymagana)
- Rekordy zgód są przechowywane ze znacznikami czasu
Przechowywanie danych
- Szyfrowanie bazy danych w spoczynku jest włączone
- TLS jest wymuszony dla wszystkich połączeń
- Wrażliwe pola mają szyfrowanie na poziomie aplikacji
- Kopie zapasowe są zaszyfrowane
- Dostęp do danych produkcyjnych jest ograniczony i logowany
Prawa użytkowników
- Endpoint eksportu danych istnieje i obejmuje wszystkie tabele
- Endpoint usunięcia konta istnieje i obsługuje wszystkie dane
- Użytkownicy mogą przeglądać i wycofywać zgody w ustawieniach
- Usunięcie jest propagowane do usług zewnętrznych
- Wszystkie żądania praw użytkowników są obsługiwane w ciągu 30 dni
Pliki cookie i śledzenie
- Baner zgody na pliki cookie jest zaimplementowany
- Nieistotne pliki cookie są blokowane przed zgodą
- Wybory zgody są szczegółowe (nie wszystko albo nic)
- “Odrzuć wszystkie” jest tak widoczne jak “Zaakceptuj wszystkie”
Podmioty trzecie
- Wszyscy przetwarzający dane są udokumentowani
- DPA jest podpisany z każdym przetwarzającym
- Dane wysyłane do podmiotów trzecich są zminimalizowane
- Usunięcie danych z systemów podmiotów trzecich jest możliwe
Bezpieczeństwo
- Logi audytowe śledzą dostęp do danych osobowych
- Alerty anomalii są skonfigurowane
- Plan reagowania na incydenty jest udokumentowany
- Proces powiadamiania o naruszeniach jest zdefiniowany (termin 72 godziny)
Retencja
- Okresy retencji są zdefiniowane dla wszystkich typów danych
- Automatyczne zadania czyszczenia są zaplanowane
- Wygasłe dane są faktycznie usuwane (zweryfikuj to)
Podsumowanie
Zgodność z GDPR nie jest jednorazowym projektem. To zestaw praktyk wplecionych w sposób budowania oprogramowania. Praca techniczna jest prosta: przechowywanie zgód, eksport danych, endpointy usuwania, szyfrowanie, logowanie audytowe. Standardowa inżynieria.
Trudna część to bycie dokładnym. Łatwo zapomnieć o tym pliku logu, tym zdarzeniu analitycznym lub tej integracji z usługą zewnętrzną przechowującą e-maile użytkowników. Audytuj regularnie. Testuj swój endpoint usuwania. Weryfikuj kompletność eksportów. Wbuduj prywatność w swój proces od samego początku.
Potrzebujesz pomocy przy budowaniu oprogramowania zgodnego z GDPR lub audycie istniejących systemów? Skontaktuj się z nami. Budujemy aplikacje stawiające prywatność na pierwszym miejscu dla europejskich firm i przedsiębiorstw obsługujących użytkowników z UE.