Prywatność od samego początku w Pensylwanii: obowiązek regulacyjny i wybór architektoniczny

Ogólne rozporządzenie o ochronie danych (RODO), które weszło w życie 25 maja 2018 r., nie ogranicza się do nakładania obowiązków biurokratyczne dla administracji publicznej: art. 25 stanowi, że ochrona danych musi być zintegrowana już na etapie projektowania systemów informatycznych i procesów przetwarzających dane osobowe. Ta zasada, znany jako Prywatność już w fazie projektowania, zamienia zgodność z przepisami w jedno jakość architektoniczna oprogramowania.

Dla programisty lub architekta pracującego na włoskich systemach PA - rejestr cyfrowy, elektroniczna karta zdrowia, portale dostępu do usług z SPID lub CIE, systemy zarządzania procedurami administracyjnymi - zrozumieć i wdrożyć poprawnie RODO-by-Design nie jest opcjonalne. Sankcje przewidziane przez Gwaranta Organu Ochrony Danych Osobowych mogą osiągnąć 4% światowych rocznych obrotów podmiotów prywatnych; w przypadku agencji płatniczych konsekwencje obejmują blokady dostępu do Internetu leczenia, odszkodowania za szkody i znaczną utratę reputacji.

Czego dowiesz się w tym artykule

  • 7 podstawowych zasad Privacy by Design i sposoby przekładania ich na konkretne decyzje architektoniczne
  • Wzorce pseudonimizacji i anonimizacji baz danych i rządowych interfejsów API
  • Implementacja wzorca minimalizacji danych w mikroserwisach PA
  • Zarządzanie zgodami: architektura i wdrożenie systemu zgodnego z RODO
  • Automatyczne przechowywanie i prawo do usunięcia danych w relacyjnych bazach danych
  • Zgodne rejestrowanie audytu: jak śledzić zabiegi bez naruszania prywatności
  • DPIA (Ocena Skutków Ochrony Danych) dla systemów wysokiego ryzyka

7 podstawowych zasad i ich wpływ na architekturę

Ann Cavoukian, była komisarz Ontario ds. informacji i prywatności, sformalizowała 7 zasad prywatności od samego początku, które RODO zostało wdrożone. Każda zasada przekłada się na konkretne wybory architektoniczne:

Zasada PbD Artykuł RODO Wzór architektoniczny Techniki betonowe
Proaktywne, a nie reaktywne Artykuł 25 Prywatność modelowania zagrożeń Ocena ryzyka dla prywatności w fazie projektowania
Prywatność jako domyślna Artykuł 25 ust. 2 Domyślnie wyrażaj zgodę Wyraźna zgoda, domyślnie minimalizacja
Zintegrowany z projektem Artykuł 25 ust. 1 Architektura oparta na prywatności Pseudonimizacja, szyfrowanie w stanie spoczynku
Pełna funkcjonalność Artykuł 5 Unikanie o sumie zerowej Prywatność + bezpieczeństwo nie są ze sobą sprzeczne
Kompleksowe bezpieczeństwo Artykuł 32 Głęboka obrona TLS, szyfrowanie, zarządzanie kluczami
Widoczność i przejrzystość Artykuł 13/14 Ścieżka audytu + ujawnienie Anonimowe logowanie, informacja o ochronie prywatności
Szacunek dla prywatności użytkowników Artykuł 7/8/17 Sterowanie zorientowane na użytkownika Interfejs użytkownika zgody, prawo do bycia zapomnianym, przenośność

Wzorzec 1: Minimalizacja danych w mikrousługach PA

Zasada minimalizacji (art. 5 ust. 1 lit. c RODO) wymaga, aby gromadzone dane były „adekwatne, istotne i ograniczone do tego, co jest to niezbędne do celów, w jakich są przetwarzane”. W architekturze mikrousług przekłada się to na wzorzec Minimalizacja danych na granicy: każda usługa musi otrzymać tylko te dane, których potrzebuje do działania jego specyficzną funkcję.

Rozważ skorzystanie z usługi weryfikacji tożsamości, aby uzyskać dostęp do świadczeń zdrowotnych. Usługa nie potrzebuje nazwy pełna informacja obywatela w celu sprawdzenia, czy przysługuje mu świadczenie: wystarczy mu anonimowy identyfikator i status uprawniony. Wzorzec jest implementowany poprzez Selektywne DTO (obiekty przesyłania danych). e zapytania projekcyjne.

# Pattern Data Minimization - Esempio Python/FastAPI
# Scenario: servizio prestazioni sanitarie PA

from pydantic import BaseModel
from typing import Optional
import hashlib

# SBAGLIATO: trasferisce dati non necessari
class CitizenFullDTO(BaseModel):
    citizen_id: str
    fiscal_code: str
    first_name: str      # Non necessario per verifica
    last_name: str       # Non necessario per verifica
    birth_date: str      # Non necessario per verifica
    address: str         # Non necessario per verifica
    phone: str           # Non necessario per verifica

# CORRETTO: minimizza i dati al minimo necessario
class CitizenEligibilityDTO(BaseModel):
    # Solo un token opaco, non il codice fiscale reale
    pseudonymous_id: str
    is_eligible: bool
    benefit_category: str
    # Nessun dato personale identificativo

# Service layer con minimizzazione
class HealthBenefitService:
    def __init__(self, citizen_repo, pseudonym_service):
        self.citizen_repo = citizen_repo
        self.pseudonym_service = pseudonym_service

    def check_eligibility(self, pseudonymous_id: str, benefit_code: str) -> CitizenEligibilityDTO:
        # Risolve il pseudonimo solo internamente, non lo espone
        real_id = self.pseudonym_service.resolve(pseudonymous_id)

        # Query mirata: solo il dato necessario
        is_eligible = self.citizen_repo.check_benefit_eligibility(
            citizen_id=real_id,
            benefit_code=benefit_code
        )

        return CitizenEligibilityDTO(
            pseudonymous_id=pseudonymous_id,
            is_eligible=is_eligible,
            benefit_category=benefit_code
        )
        # NON ritorna: nome, CF, indirizzo, telefono, email

# Repository con projection query (minimizzazione lato DB)
class CitizenRepository:
    def check_benefit_eligibility(self, citizen_id: str, benefit_code: str) -> bool:
        # SELECT solo la colonna necessaria, non SELECT *
        query = """
            SELECT EXISTS(
                SELECT 1 FROM citizen_benefits
                WHERE citizen_id = {citizen_id}
                AND benefit_code = {benefit_code}
                AND valid_until >= CURRENT_DATE
            )
        """
        return self.db.execute(query, {"citizen_id": citizen_id, "benefit_code": benefit_code}).scalar()

Wzór 2: Pseudonimizacja i tokenizacja

Pseudonimizacja (zdefiniowana w art. 4 ust. 5 RODO) jest jednym ze środków technicznych, o których wyraźnie mowa w art. 25 polubień adekwatne do wykazania zgodności z zasadą Privacy by Design. Różni się od anonimizacji: Dane pseudonimizowane mogą można prześledzić aż do zainteresowanej strony za pomocą dodatkowych informacji przechowywanych oddzielnie, w przeciwieństwie do danych anonimowych mogą (a zatem nie podlegają RODO).

W przypadku systemów PA pseudonimizacja jest często lepsza niż anonimizacja, ponieważ pozwala zachować wewnętrzna identyfikowalność (niezbędna do audytu i zapewnienia zgodności) przy jednoczesnej ochronie tożsamości osób, których dane dotyczą, w systemach z przodu i w logach.

# Pattern Pseudonimizzazione con Vault separato
# Architettura: Pseudonym Vault isolato dal sistema principale

import secrets
import hmac
import hashlib
from datetime import datetime, timedelta
from dataclasses import dataclass

@dataclass
class PseudonymRecord:
    pseudonym: str
    real_id: str
    created_at: datetime
    expires_at: datetime
    purpose: str  # Finalità del trattamento (Art. 5(1)(b))

class PseudonymVault:
    """
    Vault isolato che gestisce la mappatura pseudonimo <-> identità reale.
    Accesso ristretto: solo servizi autorizzati con chiave vault.
    Log di ogni accesso per audit GDPR.
    """

    def __init__(self, vault_key: bytes, db_connection):
        self._vault_key = vault_key
        self._db = db_connection

    def create_pseudonym(
        self,
        real_id: str,
        purpose: str,
        validity_days: int = 365
    ) -> str:
        """
        Genera pseudonimo crittograficamente sicuro.
        Usa HMAC-SHA256 con chiave vault per essere deterministico
        (stesso real_id + purpose = stesso pseudonimo) ma non invertibile
        senza la chiave vault.
        """
        # HMAC deterministico: stesso input = stesso output
        # Ma non reversibile senza vault_key
        pseudonym_bytes = hmac.new(
            key=self._vault_key,
            msg=f"{real_id}:{purpose}".encode(),
            digestmod=hashlib.sha256
        ).digest()

        # Converti in stringa URL-safe
        pseudonym = pseudonym_bytes.hex()[:32]  # 128-bit, abbondante

        # Registra nel vault (separato dal DB principale)
        record = PseudonymRecord(
            pseudonym=pseudonym,
            real_id=real_id,
            created_at=datetime.utcnow(),
            expires_at=datetime.utcnow() + timedelta(days=validity_days),
            purpose=purpose
        )
        self._db.insert_pseudonym(record)

        return pseudonym

    def resolve_pseudonym(self, pseudonym: str, requesting_service: str) -> str:
        """
        Risolve pseudonimo -> identità reale.
        Richiede autorizzazione esplicita del servizio richiedente.
        Logga ogni accesso per audit.
        """
        # Audit log obbligatorio
        self._log_resolution_access(
            pseudonym=pseudonym,
            requesting_service=requesting_service,
            timestamp=datetime.utcnow()
        )

        record = self._db.get_pseudonym(pseudonym)

        if not record:
            raise ValueError("Pseudonym not found")

        if datetime.utcnow() > record.expires_at:
            raise ValueError("Pseudonym expired")

        return record.real_id

    def _log_resolution_access(self, pseudonym: str, requesting_service: str, timestamp: datetime):
        """
        Log GDPR-compliant: registra chi ha risolto quale pseudonimo e quando.
        Il log stesso usa solo lo pseudonimo (non l'identità reale).
        """
        self._db.insert_audit_log({
            "action": "PSEUDONYM_RESOLVED",
            "pseudonym_hash": hashlib.sha256(pseudonym.encode()).hexdigest(),
            "requesting_service": requesting_service,
            "timestamp": timestamp.isoformat(),
            "legal_basis": "GDPR Art. 6(1)(e) - Task public interest"
        })

Wzór 3: Zarządzanie zgodami zgodnymi z RODO

Zgoda (art. 7 RODO) musi być bezpłatne, konkretne, świadome i jednoznaczne. W przypadku AP w większości W niektórych przypadkach przetwarzanie nie opiera się na zgodzie, lecz na innych podstawach prawnych (art. 6 ust. 1 lit. e): zadanie interesu publicznego, o Sztuka. 6 ust. 1 lit. c): obowiązek prawny). Jednakże, gdy zgoda jest wybraną podstawą prawną – na przykład w przypadku komunikacji marketing, newslettery czy opcjonalne przetwarzanie – system zarządzania zgodami musi spełniać precyzyjne wymagania.

Un Platforma zarządzania zgodami (CMP) dla AP musi zapamiętać: co zostało przyjęte, kiedy i z czym wersję informacji, z jakiego kanału i musi umożliwiać odwołanie tak proste, jak było to postanowienie.

-- Schema SQL: Consent Management GDPR-compliant
-- Database separato o schema isolato con accesso controllato

CREATE TABLE consent_purposes (
    purpose_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    code VARCHAR(64) UNIQUE NOT NULL,
    name_it TEXT NOT NULL,
    name_en TEXT,
    description_it TEXT NOT NULL,
    legal_basis VARCHAR(32) NOT NULL,  -- 'consent', 'legal_obligation', 'public_task'
    data_categories TEXT[] NOT NULL,   -- Categorie di dati trattati
    retention_days INTEGER NOT NULL,
    third_parties TEXT[],              -- Destinatari (trasparenza)
    created_at TIMESTAMPTZ DEFAULT NOW(),
    version INTEGER NOT NULL DEFAULT 1,
    is_active BOOLEAN DEFAULT TRUE
);

CREATE TABLE citizen_consents (
    consent_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    -- Pseudonimo, non ID reale
    citizen_pseudonym VARCHAR(64) NOT NULL,
    purpose_id UUID REFERENCES consent_purposes(purpose_id),
    status VARCHAR(16) NOT NULL CHECK (status IN ('granted', 'denied', 'withdrawn')),
    granted_at TIMESTAMPTZ,
    withdrawn_at TIMESTAMPTZ,
    -- Prova del consenso (Art. 7(1): titolare deve dimostrare il consenso)
    proof_channel VARCHAR(32) NOT NULL,    -- 'web_portal', 'mobile_app', 'paper'
    proof_ip_hash VARCHAR(64),             -- Hash dell'IP, non IP raw
    proof_session_id VARCHAR(128),
    privacy_policy_version VARCHAR(16) NOT NULL,
    -- Lingua in cui è stata mostrata l'informativa
    consent_language VARCHAR(8) NOT NULL DEFAULT 'it',
    user_agent_hash VARCHAR(64)
);

-- Indice per query rapide sulla revoca
CREATE INDEX idx_consents_pseudonym ON citizen_consents(citizen_pseudonym, status);

-- Vista per audit: mostra solo dati necessari al DPO
CREATE VIEW consent_audit_view AS
SELECT
    c.consent_id,
    c.citizen_pseudonym,
    p.code AS purpose_code,
    p.name_it AS purpose_name,
    c.status,
    c.granted_at,
    c.withdrawn_at,
    c.proof_channel,
    c.privacy_policy_version
FROM citizen_consents c
JOIN consent_purposes p ON c.purpose_id = p.purpose_id;

-- Funzione per il diritto di revoca (Art. 7(3))
CREATE OR REPLACE FUNCTION withdraw_consent(
    p_citizen_pseudonym VARCHAR(64),
    p_purpose_code VARCHAR(64)
) RETURNS VOID AS $
BEGIN
    UPDATE citizen_consents
    SET
        status = 'withdrawn',
        withdrawn_at = NOW()
    WHERE
        citizen_pseudonym = p_citizen_pseudonym
        AND purpose_id = (
            SELECT purpose_id FROM consent_purposes WHERE code = p_purpose_code
        )
        AND status = 'granted';

    -- Logga l'evento per audit
    INSERT INTO consent_events (
        event_type, citizen_pseudonym, purpose_code, occurred_at
    ) VALUES (
        'CONSENT_WITHDRAWN', p_citizen_pseudonym, p_purpose_code, NOW()
    );
END;
$ LANGUAGE plpgsql;

Wzór 4: Automatyczne przechowywanie danych i prawo do ich usunięcia

Zasada „ograniczenia przechowywania” (art. 5 ust. 1 lit. e RODO) wymaga, aby dane osobowe były przechowywane "w formie umożliwiającej identyfikację zainteresowanych na okres nie dłuższy niż osiągnięcie od celów, dla których są przetwarzane„. W praktyce każdy system PA musi wdrożyć politykę retencji automatyczny, który usuwa lub anonimizuje wygasłe dane bez ręcznej interwencji.

Prawo do usunięcia danych (art. 17 RODO), znane jako „prawo do bycia zapomnianym”, dodaje dodatkową warstwę złożoności: system musi mieć możliwość usunięcia danych konkretnej osoby na żądanie, z zastrzeżeniem wszelkich wyjątków (przykładowo dane niezbędne do wywiązania się z obowiązków prawnych).

# Data Retention Manager - Python con scheduling automatico
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from dataclasses import dataclass
from enum import Enum
from datetime import datetime, timedelta
from typing import List

class RetentionAction(Enum):
    DELETE = "delete"
    ANONYMIZE = "anonymize"
    ARCHIVE = "archive"

@dataclass
class RetentionPolicy:
    table_name: str
    date_column: str
    retention_days: int
    action: RetentionAction
    legal_basis: str  # Documentazione dell'obbligo legale

# Registro delle politiche di retention (da configurazione, non hardcoded)
RETENTION_POLICIES: List[RetentionPolicy] = [
    RetentionPolicy(
        table_name="citizen_session_logs",
        date_column="created_at",
        retention_days=90,  # 3 mesi per log sessione
        action=RetentionAction.DELETE,
        legal_basis="GDPR Art. 5(1)(e) - Storage limitation"
    ),
    RetentionPolicy(
        table_name="service_requests",
        date_column="completed_at",
        retention_days=2555,  # 7 anni per atti amministrativi
        action=RetentionAction.ANONYMIZE,
        legal_basis="D.Lgs. 82/2005 Art. 40 - Conservazione atti"
    ),
    RetentionPolicy(
        table_name="consent_records",
        date_column="withdrawn_at",
        retention_days=365,  # 1 anno dopo revoca per prova
        action=RetentionAction.DELETE,
        legal_basis="GDPR Art. 7(1) - Prova consenso"
    ),
]

class DataRetentionManager:
    def __init__(self, db, audit_logger):
        self.db = db
        self.audit_logger = audit_logger
        self.scheduler = AsyncIOScheduler()

    async def run_retention_policy(self, policy: RetentionPolicy) -> dict:
        """Esegue una politica di retention e ritorna statistiche."""
        cutoff_date = datetime.utcnow() - timedelta(days=policy.retention_days)
        stats = {"policy": policy.table_name, "action": policy.action.value, "rows_affected": 0}

        if policy.action == RetentionAction.DELETE:
            result = await self.db.execute(
                f"DELETE FROM {policy.table_name} WHERE {policy.date_column} < $1",
                cutoff_date
            )
            stats["rows_affected"] = result.rowcount

        elif policy.action == RetentionAction.ANONYMIZE:
            # Anonymize: sostituisce dati identificativi con hash o NULL
            result = await self.db.execute(
                f"""UPDATE {policy.table_name}
                    SET
                        fiscal_code = 'ANONYMIZED_' || gen_random_uuid()::text,
                        first_name = NULL,
                        last_name = NULL,
                        email = NULL,
                        phone = NULL
                    WHERE {policy.date_column} < $1
                    AND fiscal_code NOT LIKE 'ANONYMIZED_%'""",
                cutoff_date
            )
            stats["rows_affected"] = result.rowcount

        # Audit log obbligatorio per ogni operazione di retention
        await self.audit_logger.log({
            "event": "RETENTION_EXECUTED",
            "policy": policy.table_name,
            "action": policy.action.value,
            "cutoff_date": cutoff_date.isoformat(),
            "rows_affected": stats["rows_affected"],
            "legal_basis": policy.legal_basis,
            "executed_at": datetime.utcnow().isoformat()
        })

        return stats

    async def handle_erasure_request(self, citizen_pseudonym: str, request_id: str) -> dict:
        """
        Gestisce il diritto alla cancellazione (Art. 17 GDPR).
        Verifica le eccezioni prima di procedere.
        """
        # Verifica eccezioni Art. 17(3) - obblighi legali, interesse pubblico, etc.
        exceptions = await self._check_erasure_exceptions(citizen_pseudonym)

        if exceptions:
            # Non cancella, documenta il motivo del diniego
            await self.audit_logger.log({
                "event": "ERASURE_REQUEST_DENIED",
                "citizen_pseudonym": citizen_pseudonym,
                "request_id": request_id,
                "reasons": exceptions
            })
            return {"status": "denied", "reasons": exceptions}

        # Procede con la cancellazione
        tables_affected = []
        for table in ["citizen_session_logs", "consent_records", "service_requests_temp"]:
            rows = await self.db.execute(
                f"DELETE FROM {table} WHERE citizen_pseudonym = $1",
                citizen_pseudonym
            )
            if rows.rowcount > 0:
                tables_affected.append(table)

        await self.audit_logger.log({
            "event": "ERASURE_REQUEST_EXECUTED",
            "citizen_pseudonym": citizen_pseudonym,
            "request_id": request_id,
            "tables_affected": tables_affected,
            "executed_at": datetime.utcnow().isoformat()
        })

        return {"status": "completed", "tables_affected": tables_affected}

DPIA: kiedy jest obowiązkowa i jak ją zorganizować

La Ocena skutków dla ochrony danych (DPIA, ocena skutków dla ochrony danych) jest obowiązkowa zgodnie z art. 35 RODO, gdy przetwarzanie”może stanowić wysokie ryzyko dla praw i wolności osób osoby fizyczne„. W przypadku włoskiej Agencji Ochrony Prywatności Gwarant Prywatności opublikował listę wymaganych zabiegów DPIA jest obowiązkowa.

Zabiegi PA wymagające obowiązkowej DPIA

  • Systematyczne profilowanie obywateli (np. punktacja społeczno-ekonomiczna)
  • Leczenie na dużą skalę poszczególnych kategorii danych (zdrowotnych, sądowych)
  • Systematyczny nadzór przestrzeni publicznych (monitoring wideo)
  • Systemy dopasowywania/łączenia zbiorów danych pochodzących z różnych źródeł
  • Dane osób bezbronnych (małoletni, pacjenci, osoby ubiegające się o azyl)
  • Innowacyjne wykorzystanie technologii (AI/ML w przypadku decyzji zautomatyzowanych, art. 22)
  • Transfer międzynarodowy danych osobowych

Ustrukturyzowana DPIA obejmuje: opis przetwarzania i jego celów, ocenę konieczności i proporcjonalności, identyfikacja i ocena zagrożeń dla praw osób, których dane dotyczą, oraz środki przewidziane w celu zaradzenia tym zagrożeniom (w tym zabezpieczenia i mechanizmy bezpieczeństwa). Zaangażowanie IOD (Inspektora Ochrony Danych) jest obowiązkowe (art. 35 ust. 2) oraz DPIA muszą zostać zaktualizowane w przypadku zmiany sposobu leczenia.

Wzór 5: Rejestrowanie audytu zgodne z RODO

Niedocenianym wymogiem RODO jest odpowiedzialność (odpowiedzialność, art. 5 ust. 2): właściciel leczenia musi być w stanie wykazać przestrzeganie zaleceń. Wymaga to systemu rejestrowania audytu, który śledzi operacje na danych osobowych, ale które – paradoksalnie – same w sobie nie stają się źródłem nadmiernego przetwarzania danych osobowych.

Dziennik audytu zgodny z RODO musi:

  • Nagrywać Kto uzyskiwał dostęp do jakich danych i kiedy, w miarę możliwości używając pseudonimów lub identyfikatorów systemowych
  • Być niezmienny (tylko do dołączenia), aby zapewnić integralność dowodów
  • Posiadaj własną politykę przechowywania (logi nie są przechowywane na zawsze)
  • Bądź chroniony przed nieautoryzowanym dostępem (oddzielny od głównego DB)
  • Obsługuj wydajne zapytania w celu odpowiedzi na wnioski DPO lub Organu Gwaranta
# Audit Logger immutabile con PostgreSQL - append-only tramite trigger
# Usa una tabella separata con accesso write-only dall'applicazione

-- Schema audit log (database o schema separato)
CREATE TABLE gdpr_audit_log (
    log_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    -- Identificatori: pseudonimizzati dove possibile
    subject_pseudonym VARCHAR(64),       -- Chi ha eseguito l'azione
    affected_entity_pseudonym VARCHAR(64), -- Su quale entità
    -- Cosa è successo
    action_type VARCHAR(64) NOT NULL,    -- READ, UPDATE, DELETE, EXPORT, etc.
    resource_type VARCHAR(64) NOT NULL,  -- citizen_record, consent, health_data, etc.
    -- Contesto
    legal_basis VARCHAR(128),            -- Base giuridica del trattamento
    purpose VARCHAR(256),                -- Finalità del trattamento
    -- Metadati tecnici (senza dati personali)
    service_name VARCHAR(128) NOT NULL,
    request_id VARCHAR(128),
    -- Timestamp immutabile
    occurred_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
    -- NON includere: IP raw, user agent completo, payload della request
);

-- Nega UPDATE e DELETE sulla tabella audit (append-only)
CREATE RULE no_update_audit AS ON UPDATE TO gdpr_audit_log DO INSTEAD NOTHING;
CREATE RULE no_delete_audit AS ON DELETE TO gdpr_audit_log DO INSTEAD NOTHING;

-- Funzione trigger per log automatico su tabelle sensibili
CREATE OR REPLACE FUNCTION audit_sensitive_access() RETURNS TRIGGER AS $
BEGIN
    INSERT INTO gdpr_audit_log (
        action_type,
        resource_type,
        subject_pseudonym,
        affected_entity_pseudonym,
        service_name,
        legal_basis
    ) VALUES (
        TG_OP,                          -- INSERT/UPDATE/DELETE
        TG_TABLE_NAME,
        current_setting('app.current_user_pseudonym', true),
        NEW.citizen_pseudonym,
        current_setting('app.service_name', true),
        current_setting('app.legal_basis', true)
    );
    RETURN NEW;
END;
$ LANGUAGE plpgsql SECURITY DEFINER;

-- Applica il trigger alle tabelle sensibili
CREATE TRIGGER audit_health_records
    AFTER INSERT OR UPDATE OR DELETE ON health_records
    FOR EACH ROW EXECUTE FUNCTION audit_sensitive_access();

Ogólna architektura: RODO według projektu w usłudze PA

Łącząc wszystkie opisane wzorce, architektura usługi PA zgodnej z RODO-by-Design obejmuje:

  • Brama API: Punkt wejścia weryfikujący uwierzytelnienie (SPID/CIE), stosujący pseudonimizację i propaguje podstawę prawną w kontekście żądania (poprzez nagłówek lub oprogramowanie pośredniczące)
  • Pseudonim Skarbiec: wydzielona usługa mapowania pseudonimów i tożsamości, z ograniczonym dostępem i dziennik audytu każdej uchwały
  • Mikrousługi z minimalizacją danych: każda usługa otrzymuje tylko niezbędne dane, ujawnia minimalne DTO, użyj zapytań projekcyjnych do bazy danych
  • Usługa zarządzania zgodami: zarządza zgodami, weryfikuje podstawy prawne, wspiera cofnięcie i eksport danych (przenośność art. 20)
  • Harmonogram przechowywania: zadania okresowe, które stosują zasady przechowywania i zarządzają żądaniami anulowania i sporządzania raportów dla IOD
  • Usługa dziennika audytu: tylko do dołączania, oddzielne, z dostępem tylko do zapisu z aplikacji i tylko do odczytu przez IOD/Gwaranta

Przydatne narzędzia i ramy dla RODO-by-Design w PA

  • OpenDP: Biblioteka Pythona zapewniająca zróżnicowaną prywatność, przydatna w przypadku anonimowych analiz
  • Narzędzie do anonimizacji danych ARX: narzędzie open source do anonimizacji zbiorów danych
  • Płaszcz na klucze: Dostawca tożsamości typu open source z wbudowaną obsługą zarządzania zgodami
  • Szkic danych: do hashowania i MinHash, przydatne w skalowalnej pseudonimizacji
  • PgAudyt: Rozszerzenie PostgreSQL do rejestrowania audytu na poziomie bazy danych
  • Projektanci Włochy: oficjalne wytyczne AgID dotyczące prywatności już w fazie projektowania dla PA
  • Przewodnik CNIL dotyczący prywatności według projektu: praktyczna metodologia władz francuskich, stosowana we Włoszech

Rola DPO i współpraca z zespołem deweloperskim

Zgodnie z art. 37 RODO wyznaczenie Inspektora Ochrony Danych (IOD) jest obowiązkowe dla wszystkich organów publicznych oraz organy publiczne (z wyjątkiem sądów pełniących funkcje jurysdykcyjne). IOD nie tylko osoba zajmująca się zgodnością: w przypadku zespołów programistycznych reprezentuje wewnętrznego konsultanta ds. ochrony danych, który muszą być zaangażowani od początkowych etapów projektu.

W praktyce zespół programistów powinien:

  • Zaangażuj DPO w przegląd architektury podczas przetwarzania danych osobowych
  • Dokumentuj wybory projektowe dotyczące prywatności w rekordach decyzji architektonicznych (ADR)
  • Uwzględnij kontrole zgodności z RODO w definicjach historii użytkownika
  • Przeprowadź ocenę ryzyka dla prywatności dla każdej nowej funkcji wprowadzającej przetwarzanie danych osobowych
  • W miarę możliwości aktualizuj rejestr przetwarzania (art. 30) w sposób zautomatyzowany

Wnioski i dalsze kroki

RODO-by-Design nie jest biurokratyczną listą kontrolną, którą należy odhaczyć na koniec prac rozwojowych: jest to podejście architektoniczne które, jeśli zostaną zintegrowane na wczesnych etapach projektu, pozwolą stworzyć bezpieczniejsze, bardziej przejrzyste i solidniejsze systemy. Wzorce opisane w tym artykule — minimalizacja danych, pseudonimizacja, zarządzanie zgodami, retencja automatyczne, rejestrowanie audytowe – dotyczą każdej usługi cyfrowej włoskiej Agencji Ochrony Środowiska, począwszy od prostej strona zbierania zgód na biuletyn instytucjonalny aż po złożone systemy, takie jak karta zdrowia platformy płatności elektronicznych lub cyfrowych.

W kolejnym artykule z tej serii przeanalizujemy, jak zaimplementować dostępne interfejsy użytkownika dla drugiego PA standard WCAG 2.1 AA: kolejny wymóg regulacyjny, który podobnie jak RODO nagradza tych, którzy integrują go już na etapie projektowania.

Powiązane artykuły z tej serii

  • GovTech #01: eIDAS 2.0 i portfel EUDI – europejska tożsamość cyfrowa i weryfikowalne dane uwierzytelniające
  • GovTech #02: OpenID Connect dla tożsamości rządowej - najlepsze praktyki SPID, CIE i bezpieczeństwa
  • GovTech #05: Dostępny interfejs użytkownika dla implementacji PA - WCAG 2.1 AA
  • GovTech #06: Integracja API rządowych - SPID, CIE i pagoPA